From 92ceb0e8fb49a7ebfad946e3278c6d9db97566a9 Mon Sep 17 00:00:00 2001 From: Dominic Fischer Date: Fri, 13 Nov 2020 18:59:40 +0000 Subject: [PATCH 001/248] 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 002/248] 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 003/248] 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 004/248] 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 005/248] 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 006/248] 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 007/248] 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 b84d7f0834d9853ad37878b31a9d219a0c28fb52 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 27 Nov 2020 20:42:02 +0100 Subject: [PATCH 008/248] Version** --- CHANGES.md | 27 +++++++++++++++++++++++++++ vector/build.gradle | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index b031e626eb..e48281081b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,30 @@ +Changes in Element 1.0.12 (2020-XX-XX) +=================================================== + +Features ✨: + - + +Improvements 🙌: + - + +Bugfix 🐛: + - + +Translations 🗣: + - + +SDK API changes ⚠️: + - + +Build 🧱: + - + +Test: + - + +Other changes: + - + Changes in Element 1.0.11 (2020-11-27) =================================================== diff --git a/vector/build.gradle b/vector/build.gradle index 037b049a76..561e1fd824 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -17,7 +17,7 @@ androidExtensions { // Note: 2 digits max for each value ext.versionMajor = 1 ext.versionMinor = 0 -ext.versionPatch = 11 +ext.versionPatch = 12 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' From cf70916764b3b5a3347e38333877c4256e7f8f80 Mon Sep 17 00:00:00 2001 From: Valere Date: Sat, 28 Nov 2020 00:41:29 +0100 Subject: [PATCH 009/248] 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 217c88f34236614d20cb416201744f838cc999f7 Mon Sep 17 00:00:00 2001 From: zeritti Date: Sat, 28 Nov 2020 09:45:41 +0000 Subject: [PATCH 010/248] Translated using Weblate (Czech) Currently translated at 100.0% (1986 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/cs/ --- vector/src/main/res/values-cs/strings.xml | 61 ++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values-cs/strings.xml b/vector/src/main/res/values-cs/strings.xml index dd6a401164..ea8cff2da2 100644 --- a/vector/src/main/res/values-cs/strings.xml +++ b/vector/src/main/res/values-cs/strings.xml @@ -1673,7 +1673,7 @@ Odešle daný emote zabarvený jako duha Časová osa Editor zpráv - Zapnout šifrování end-to-end + Zapnout šifrování end-to-end… Jakmile zapnuto, šifrování nelze vypnout. Zapnout šifrování\? Jakmile zapnuto, šifrování místnosti nelze vypnout. Zprávy odeslané v zašifrované místnosti nemohou být čteny serverem, ale pouze účastníky místnosti. Zapnutím šifrování mohou boty a můstky přestat správně pracovat. @@ -2225,4 +2225,63 @@ Nemáte oprávnění k zahájení hovoru Nemáte oprávnění k zahájení konferenčního hovoru Resetovat + Odkaz na Matrix + QR kód nebyl oskenován! + Neplatný QR kód (neplatné URI)! + Sobě nelze zaslat přimou zprávu! + Sdílet textem + Hledat kontakty v Matrixu + Nastavit avatara + Souhlas uživatele nebyl udělen. + Sdělte tento kód lidem, aby s Vámi mohli po skenování zahájit konverzaci. + Můj kód + Sdílet můj kód + Skenovat QR kód + To není platný matrixový QR kód + 🔐️ Přidej se ke mně v elementu + Ahoj, mluv se mnou v Elementu: %s + Pozvat přátele + Přidat lidi + "Téma: " + Přidat téma + %s, abyste dali lidem vědět, o čem tato místnost je. + Toto je počátek Vaší historie přímých zpráv s %s. + Toto je počátek této konverzace. + Toto je počátek %s. + Exportovat audit + K zapnutí šifrování v této místnosti nemáte oprávnění. + Přímá zpráva + Zakládám místnost… + Některé znaky nejsou dovoleny + Prosím, zadejte adresu místnosti + Tato adresa je již obsazena + Adresa místnosti + Můžete zapnout, pokud bude tato místnost využita pro spolupráci interních týmů na Vašem homeserveru. Později nelze změnit. + Trvale blokovat vstup do této místnosti všem, kdo nejsou členy %s + Skrýt pokročilé + Ukázat pokročilé + %1$d z %2$d + Založit novou přímou konverzaci pomocí Matrix ID + Založit novou přímou konverzaci pomocí skenu QR kódu + Abyste nalezli existující kontakty, jež znáte, souhlasíte s odesláním svých kontaktních údajů (telefonní čísla a/nebo emailové adresy) na nastavený server pro identity (%1$s)\? +\n +\nZa účelem soukromí budou data před odesláním hašována. + Poslat emailové adresy a telefonní čísla + Udělit souhlas + Zrušit můj souhlas + Neudělili jste souhlas pro odeslání emailových adres a telefonních čísel na tento server pro identity za účelem nalezení dalších uživatelů podle svých kontaktů. + Udělili jste souhlas pro odeslání emailových adres a telefonních čísel na tento server pro identity za účelem nalezení dalších uživatelů podle svých kontaktů. + Poslat emailové adresy a telefonní čísla + Doporučení + Kontakty + Známí uživatelé + Poslední + QR kód + Přidat pomocí QR kódu + Hledat podle jména nebo ID + Udělte právo přístupu ke kontaktům. + Pro sken QR kódu je nutné povolit přístup k fotoaparátu. + Poslat historii požadavků na sdílení klíčů + Žádné další výsledky + Zahájit chat \ No newline at end of file From f7f7e808f2eaf12341e796d364875582090ab68c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Sat, 28 Nov 2020 08:43:44 +0000 Subject: [PATCH 011/248] Translated using Weblate (Estonian) Currently translated at 100.0% (1986 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/et/ --- vector/src/main/res/values-et/strings.xml | 57 ++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values-et/strings.xml b/vector/src/main/res/values-et/strings.xml index 152f5f03e2..6bc5b3cd8c 100644 --- a/vector/src/main/res/values-et/strings.xml +++ b/vector/src/main/res/values-et/strings.xml @@ -863,7 +863,7 @@ Saadab antud emote vikerkaarevärvides Ajajoon Sõnumite kirjutamine - Võta läbiv krüptimine kasutusele + Võta läbiv krüptimine kasutusele… Kui krüptimine on juba kasutusele võetud, siis ei saa seda enam eemaldada. Kas võtame krüptimise kasutusele\? Kui kord juba kasutusele võetud, siis krüptimist enam hiljem ära lõpetada ei saa. Krüptitud sõnumeid ei saa lugeda ei vaheapealses veebiliikluses ega serveris ja vaid jututoa liikmed saavad neid lugeda. Krüptimise kasutusele võtmine võib takistada nii robotite kui sõnumisildade tööd. @@ -2191,4 +2191,59 @@ Otsevestlus Lisa kaasa võtmevahetusega seotud päringute ajalugu Rohkem otsingutulemusi pole + Kas selleks, et leida tuttavaid, oled sa nõus saatma oma kontaktteavet (telefoninumbreid ja/või e-posti aadresse) siin rakenduses seadistatud isikutuvastusserverile (%1$s)\? +\n +\nParema turvalisuse nimel me ei saada teavet mitte loetava tekstina, vaid räsina. + 🔐️ Liitu minuga vestlusrakenduses Element + Hei, palun suhtle minuga vestlusrakenduses Element: %s + Kutsu sõpru + Lisa inimesi + "Teema: " + lisa jututoa teema + Selleks et kõik teaks, millega siin jututoas tegeletakse, palun %s. + See on otsesõnumite algus kasutajaga %s. + See on vestluse algus. + Siit maalt algab %s jututuba. + Sul puuduvad õigused siin jututoas läbiva krüptimise kasutuselevõtmiseks. + Loon jututuba… + Mõned tähemärgid ei ole siin lubatud + Palun kirjuta jututoa aadress + See aadress on juba kasutusel + Jututoa aadress + Sa võid sellise võimaluse kasutusele võtta, kui seda jututuba kasutatakse vaid organisatsioonisiseste tiimide ühistööks oma koduserveri piires. Seda ei saa hiljem muuta. + Keela kõikide niisuguste kasutajate liitumine selle jututoaga, kelle kasutajakonto ei asu %s koduserveris + Peida lisaseadistused + Näita lisaseadistusi + %1$d / %2$d + Alusta QR-koodi lugemise abil uut vestlust + Alusta Matrix\'i kasutajatunnuse alusel uut vestlust + Matrix\'i link + QR-kood on lugemata! + Vigane QR-kood (vigane URI)! + Sa ei ole Muhv ega saa iseendale sõnumeid saata! + Jaga tekstina + Otsi Matrix\'i võrgust tuttavaid + Seadista tunnuspilt + Kasutaja nõusolek on puudu. + Selleks, et teised kasutajad saaks sind lisada oma kontaktiks ja alustada vestlust, jaga seda QR-koodi nendega. + Minu QR-kood + Jaga minu koodi + Loe QR-koodi + See ei ole korralik Matrix\'i QR-kood + Saada e-posti aadresse ja telefoninumbreid + Nõustu + Tühista minu nõusolek + Selleks, et leida Matrixikasutajaid oma kontaktide hulgast, sa ei ole andnud nõusolekut saata e-posti aadresse ja telefoninumbreid sellele isikutuvastusserverile. + Selleks, et leida Matrixikasutajaid oma kontaktide hulgast, oled sa andnud nõusoleku saata e-posti aadresse ja telefoninumbreid sellele isikutuvastusserverile. + Saada e-posti aadresse ja telefoninumbreid + Soovitused + Kontaktid + Tuttavad kasutajad + Hiljutised + QR-kood + Lisa QR-koodi abil + Otsi nime või Matrix\'i tunnuse alusel + Anna õigused oma kontakte lugeda. + QR-koodi lugemiseks pead selleks kaamerale õigused andma. + Alusta vestlust \ No newline at end of file From 493cd2a0e3f23c30ccad85a1ee81f305dcfc870e Mon Sep 17 00:00:00 2001 From: sblondon Date: Fri, 27 Nov 2020 14:49:03 +0000 Subject: [PATCH 012/248] Translated using Weblate (French) Currently translated at 98.8% (1963 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/fr/ --- vector/src/main/res/values-fr/strings.xml | 34 ++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values-fr/strings.xml b/vector/src/main/res/values-fr/strings.xml index d6258d45bd..41cc30447d 100644 --- a/vector/src/main/res/values-fr/strings.xml +++ b/vector/src/main/res/values-fr/strings.xml @@ -1654,7 +1654,7 @@ Envoie la réaction fournie colorée comme un arc-en-ciel Discussions Éditeur de messages - Activer le chiffrement de bout en bout + Active le chiffrement de bout en bout… Une fois qu’il est activé, le chiffrement ne peut pas être désactivé. Activer le chiffrement \? Une fois qu’il est activé, le chiffrement ne peut pas être désactivé. Les messages envoyés dans les salons chiffrés ne peuvent pas être vus par le serveur, uniquement par les participants du salon. Activer le chiffrement empêchera peut-être les robots et les passerelles de fonctionner correctement. @@ -2199,4 +2199,36 @@ Inclure l\'historique d\'échange de clés Plus aucun résultat Exporter le rapport d\'audit + %s pour permettre aux gens de connaître le sujet de ce salon. + Ceci est le début de l\'historique de vos message direct avec %s. + Ceci est le début de cette conversation. + Ceci est le début de %s. + Vous n\'avez pas le droit d\'activer le chiffrement dans ce salon. + Création du salon… + Certains caractères sont interdits + Veuillez fournir une adresse de salon + Cette adresse est déjà utilisée + Adresse du salon + Créer une nouvelle conversation directe en scannant un QR Code + %1$d de %2$d + Créer une nouvelle conversation directe avec un identifiant Matrix + Dans le but de découvrir des contacts que vous connaîtriez, acceptez-vous d\'envoyer vos données de contact (numéros de téléphone et/ou e-mails) au serveur d\'identité configuré (%1$s) \? +\n +\nPour une meilleure protection de la vie privée, les données seront condensées (hash) avant l\'envoi. + Envoyer des e-mails et des numéros de téléphone + Autoriser + Révoquer mon autorisation + Vous n\'avez pas donné votre autorisation pour envoyer des e-mails et des numéros de téléphone à ce serveur d\'identité pour découvrir d\'autres utilisateurs à partir de vos contacts. + Vous avez donné votre autorisation pour envoyer des e-mails et des numéros de téléphone à ce serveur d\'identité pour découvrir d\'autres utilisateurs à partir de vos contacts. + Envoyer des e-mails et des numéros de téléphone + Suggestions + Contacts + Utilisateurs connus + Récent + QR code + Ajouter avec un QR Code + Chercher par nom ou identifiant + Autoriser l\'accès à vos contacts. + Pour scanner un QR code, vous devez autoriser l\'accès à votre appareil photo. + Débuter la discussion \ No newline at end of file From 9531a384867bf09165af219fd004537d5a449240 Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Fri, 27 Nov 2020 17:31:38 +0000 Subject: [PATCH 013/248] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (1986 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pt_BR/ --- vector/src/main/res/values-pt-rBR/strings.xml | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values-pt-rBR/strings.xml b/vector/src/main/res/values-pt-rBR/strings.xml index c040882e80..65db55c162 100644 --- a/vector/src/main/res/values-pt-rBR/strings.xml +++ b/vector/src/main/res/values-pt-rBR/strings.xml @@ -1808,7 +1808,7 @@ Envia o emoji colorido como um arco-íris Conversas Campo de texto - Ativar a criptografia de ponta a ponta + Ativar a criptografia de ponta a ponta… Uma vez ativada, a criptografia não poderá ser desativada. Ativar criptografia\? Uma vez ativada, a criptografia de uma sala não pode ser desativada. As mensagens enviadas em uma sala criptografada não podem ser lidas pelo servidor, apenas pelos participantes desta sala. A ativação da criptografia pode impedir que muitos bots e integrações funcionem corretamente. @@ -2254,4 +2254,59 @@ Não há mais resultados Exportar auditoria Enviar mensagem + Mostrar mais + Esconder mais + Link na Matrix + %s para que as pessoas saibam do que se trata esta sala. + Digite o endereço da sala + Recentes + Código QR não escaneado! + Código QR inválido (URL inválido)! + Não é possível enviar mensagens para si mesmo! + Compartilhar por texto + Pesquisar contatos na Matrix + Definir foto + A autorização do usuário não foi fornecida. + Compartilhe este código com as pessoas, para que possam escaneá-lo, de modo a adicionar seu contato e começar a conversar. + Meu código + Compartilhar meu código + Escanear um código QR + Não é um código QR da Matrix válido + 🔐️ Junte-se a mim no Element + Ei, fale comigo no Element: %s + Convidar amigos + Adicionar pessoas + "Descrição:· " + Adicionar uma descrição + Este é o começo do seu histórico de mensagens com %s. + Este é o começo desta conversa. + Este é o início de %s. + Você não tem permissão para ativar a criptografia nesta sala. + Criando sala… + Alguns caracteres não são permitidos + Este endereço já está em uso + Endereço da sala + Você pode habilitar essa opção se a sala for usada apenas para colaboração com equipes internas em seu servidor local. Essa opção não poderá ser alterada mais tarde. + Impedir que qualquer pessoa que não faça parte de %s jamais entre nesta sala + %1$d de %2$d + Começar uma nova conversa escaneando um código QR + Começar uma nova conversa com um ID na Matrix + De modo a descobrir contatos a partir de pessoas que você conhece, você aceita enviar seus dados de contato (números de telefone e/ou e-mails) para o servidor de identidade configurado (%1$s)\? +\n +\nPara obter mais privacidade, os dados enviados serão criptografados antes de serem enviados. + Enviar e-mails e números de telefone + Autorizar + Revogar minha autorização + Você não autorizou o envio de e-mails e números de telefone para este servidor de identidade, de modo a descobrir outras pessoas a partir dos seus contatos. + Você autorizou o envio de e-mails e números de telefone para este servidor de identidade, de modo a descobrir outras pessoas a partir dos seus contatos. + Enviar e-mails e números de telefone + Sugestões + Contatos + Pessoas conhecidas + Código QR + Adicionar por código QR + Pesquise por nome ou ID + Aceite a permissão para acessar seus contatos. + Para escanear um código QR, você precisa permitir o acesso à câmera. + Começar a conversar \ No newline at end of file From 422c681e55504d9e3315de8306f8b064c3208a89 Mon Sep 17 00:00:00 2001 From: krikra01 Date: Fri, 27 Nov 2020 15:05:39 +0000 Subject: [PATCH 014/248] Translated using Weblate (Russian) Currently translated at 97.1% (1930 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/ru/ --- vector/src/main/res/values-ru/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vector/src/main/res/values-ru/strings.xml b/vector/src/main/res/values-ru/strings.xml index 5b04407645..1064748828 100644 --- a/vector/src/main/res/values-ru/strings.xml +++ b/vector/src/main/res/values-ru/strings.xml @@ -2301,4 +2301,9 @@ Добавить изображение из Тема Название Комнаты + Вы дали свое согласие на отправку электронных писем и телефонных номеров на этот сервер идентификации для обнаружения других пользователей из ваших контактов. + Добавить по QR-коду + Поиск по имени или идентификатору + Разрешить доступ к вашим контактам. + Чтобы отсканировать QR-код, вам нужно разрешить доступ к камере. \ No newline at end of file From a5079f524344cbe09528d9be03762d7f4f7aab88 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Fri, 27 Nov 2020 16:34:29 +0000 Subject: [PATCH 015/248] Translated using Weblate (Albanian) Currently translated at 99.3% (1973 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/sq/ --- vector/src/main/res/values-sq/strings.xml | 53 +++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml index ce76f7c387..93e82a7cde 100644 --- a/vector/src/main/res/values-sq/strings.xml +++ b/vector/src/main/res/values-sq/strings.xml @@ -2181,4 +2181,57 @@ Mesazh i drejtpërdrejtë Dërgo historik kërkesash për dhënie kyçesh S’ka më përfundime + Lidhje Matrix + Kod QR jo i skanuar! + Kod QR i pavlefshëm (URI e pavlefshme)! + S’mund t’i dërgoni mesazh të drejtpërdrejtë vetes! + Jepuani si tekst + Kërkoni për kontakte në Matrix + Caktoni avatar + S’është dhënë pranimi nga përdoruesi. + Jepuani këtë kod njerëzve, që të mund ta skanojnë për t’ju shtuar dhe për të filluar të bisedoni. + Kodi im + Ndaje kodin tim me të tjerët + Skanoni një kod QR + S’është kod QR Matrix i vlefshëm + Takohuni me mua në Element + Hej, bisedoni me mua në Element: %s + Ftoni shokë + Shtoni persona + "Temë: " + Shtoni një temë + %s, që t’u bëni me dije njerëzve se për çfarë është kjo dhomë. + Ky është fillimi i historikut të mesazheve tuaj të drejtpërdrejtë me %s. + Ky është fillimi i kësaj bisede. + Ky është fillimi i %s. + S’keni leje të aktivizoni fshehtëzim në këtë dhomë. + Po krijohet dhoma… + Disa nga shenja nuk lejohen + Ju lutemi, jepni një adresë dhome + Kjo adresë është e përdorur tashmë + Adresë dhome + Mund ta aktivizoni këtë, nëse dhoma do të përdoret vetëm për bashkëpunim mes ekipesh të brendshëm në shërbyesin tuaj Home. Kjo s’mund të ndryshohet më vonë. + Blloko përgjithnjë pjesëmarrjen në këtë dhomë të kujtdo që s’është pjesë e %s + Fshihi të mëtejshmet + Shfaq të mëtejshme + %1$d nga %2$d + Krijoni një bisedë të re të drejtpërdrejtë duke skanuar një kod QR + Krijoni një bisedë të re të drejtpërdrejtë përmes ID-je Matrix + Që të mund të zbulohet kontakte ekzistuese që njihni, pranoni të dërgohen të dhënat tuaja të kontaktit (numra telefonash dhe/ose email-e) te Shërbyesi i formësuar për Identitete (%1$s)\? +\n +\nPër më tepër privatësi, të dhënat e dërguara do të kodohen, para se të dërgohen. + Dërgoni email-e dhe numra telefonash + Jepe pranimin + Shfuqizoje pranimin tim + S’keni dhënë pranimin tuaj për të dërguar email-e dhe numra telefonash te ky shërbyes identitetesh që të zbulojë përdorues të tjerë prej kontakteve tuaj. + Keni dhënë pranimin tuaj për të dërguar email-e dhe numra telefonash te ky shërbyes identitetesh që të zbulojë përdorues të tjerë prej kontakteve tuaj. + Dërgo email-e dhe numra telefonash + Sugjerime + Kontakte + Përdorues të Ditur + Kod QR + Shtoni përmes kodi QR + Kërkoni sipas emri ose ID-je + Që të skanoni një kod QR, lypset të lejoni përdorim kamere. + Filloni të Llafoseni \ No newline at end of file From 88e05ffd05611eeb58f9011951f15e4c3429f7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Fri, 27 Nov 2020 22:25:31 +0000 Subject: [PATCH 016/248] Translated using Weblate (Estonian) Currently translated at 100.0% (210 of 210 strings) Translation: Element Android/Element Android Sdk Translate-URL: https://translate.element.io/projects/element-android/element-sdk/et/ --- .../src/main/res/values-et/strings.xml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/matrix-sdk-android/src/main/res/values-et/strings.xml b/matrix-sdk-android/src/main/res/values-et/strings.xml index 71ee50f075..957c0b9955 100644 --- a/matrix-sdk-android/src/main/res/values-et/strings.xml +++ b/matrix-sdk-android/src/main/res/values-et/strings.xml @@ -213,4 +213,27 @@ %1$s liitus Sina alustasid vestlust %1$s alustas vestlust + Tühi jututuba (oli %s) + + %1$s, %2$s, %3$s ja %4$d muu + %1$s, %2$s, %3$s ja %4$d muud + + %1$s, %2$s, %3$s ja %4$s + %1$s, %2$s ja %3$s + 🎉 Kõikide serverite osalemine on keelatud! Seda jututuba ei saa enam kasutada. + Muudatusi ei ole. + • Nüüd on keelatud serverid, mille ip-aadress vastab mustrile. + • Nüüd on lubatud serverid, mille ip-aadress vastab mustrile. + • Server, mille nimes leidub %s, eemaldati lubatud serverite loendist. + • Nüüd on lubatud serverid, mille nimes leidub %s. + • Server, mille nimes leidub %s eemaldati keeluloendist. + • Keelatud on server, mille nimes leidub %s. + Sina muutsid selle jututoa jaoks serverite pääsuloendit. + %s muutis selle jututoa jaoks serverite pääsuloendit. + Sina kirjeldasid selle jututoa jaoks serverite pääsuloendi. + %s kirjeldas selle jututoa jaoks serverite pääsuloendi. + • Keelatud on serverid, mille ip-aadress vastab mustrile. + • Lubatud on serverid, mille ip-aadress vastab mustrile. + • Lubatud on serverid, mille nimes leidub %s. + • Keelatud on serverid, mille nimes leidub %s. \ No newline at end of file From eb17463b68599e6834404f6308133f6ce44fcd1f Mon Sep 17 00:00:00 2001 From: sblondon Date: Fri, 27 Nov 2020 14:28:40 +0000 Subject: [PATCH 017/248] Translated using Weblate (French) Currently translated at 100.0% (210 of 210 strings) Translation: Element Android/Element Android Sdk Translate-URL: https://translate.element.io/projects/element-android/element-sdk/fr/ --- .../src/main/res/values-fr/strings.xml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/matrix-sdk-android/src/main/res/values-fr/strings.xml b/matrix-sdk-android/src/main/res/values-fr/strings.xml index 9c5fa7d3b1..f49c54a8ba 100644 --- a/matrix-sdk-android/src/main/res/values-fr/strings.xml +++ b/matrix-sdk-android/src/main/res/values-fr/strings.xml @@ -213,4 +213,27 @@ Vous avez exclus %1$s Vous avez révoqué l\'exclusion de %1$s Vous avez expulsé %1$s. Raison : %2$s + Salon vide (était %s) + + %1$s, %2$s, %3$s et %4$d autre + %1$s, %2$s, %3$s et %4$d autres + + %1$s, %2$s, %3$s et %4$s + %1$s, %2$s et %3$s + 🎉 Tous les serveurs sont interdits de participer ! Ce salon ne peut plus être utilisé. + Aucun changement. + • Les serveurs correspondant à des IP littérales sont maintenant interdits. + • Les serveurs correspondant à %s sont interdits. + • Les serveurs correspondants à des IP littérales sont interdites. + • Les serveurs correspondants à des IP littérales sont autorisés. + • Les serveurs correspondants à des IP littérales sont maintenant autorisées. + • Les serveurs correspondant à %s sont supprimés de la liste autorisée. + • les serveur correspondant à %s sont maintenant autorisés. + • Les serveurs correspondant à %s étaient supprimés de la liste des interdits. + • Les serveurs correspondant à %s sont maintenant interdits. + Vous avez changé les droits ACL du serveur pour ce salon. + %s a changé les droits ACL du serveur pour ce salon. + • Les serveurs correspondant à %s sont autorisés. + Vous avez paramétré les ACL pour ce salon. + %s paramètre les autorisations étendues (ACL) du serveur pour ce salon. \ No newline at end of file From dd50399a212255ab31106305219c2c583083f22a Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Fri, 27 Nov 2020 16:57:28 +0000 Subject: [PATCH 018/248] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (210 of 210 strings) Translation: Element Android/Element Android Sdk Translate-URL: https://translate.element.io/projects/element-android/element-sdk/pt_BR/ --- .../src/main/res/values-pt-rBR/strings.xml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml b/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml index ed9f91cdb3..e6c93cb55c 100644 --- a/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml +++ b/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml @@ -221,4 +221,27 @@ %1$s entrou Você criou a sala %1$s criou a sala + Sala vazia (era %s) + + %1$s, %2$s, %3$s e %4$d outro + %1$s, %2$s, %3$s e %4$d outros + + %1$s, %2$s, %3$s e %4$s + %1$s, %2$s e %3$s + 🎉 Todos os servidores estão proibidos de participar! Esta sala não pode mais ser usada. + Nenhuma alteração. + • Servidores correspondentes aos IP literais agora estão banidos. + • Servidores correspondentes aos IP literais agora estão permitidos. + • Servidores correspondentes à %s foram removidos da lista de permitidos. + • Servidores correspondentes à %s agora são permitidos. + • Servidores correspondente à %s foram removidos da lista de banidos. + • Servidores correspondentes à %s foram banidos. + Você alterou a lista de controle de acesso (ACL) do servidor para esta sala. + %s alterou a lista de controle de acesso (ACL) do servidor para esta sala. + • Servidores correspondentes aos IP literais estão banidos. + • Servidores correspondentes aos IP literais estão permitidos. + • Servidores correspondentes à %s estão permitidos. + • Servidores correspondentes à %s estão banidos. + Você definiu a lista de controle de acesso (ACL) do servidor para esta sala. + %s definiu a lista de controle de acesso (ACL) do servidor para esta sala. \ No newline at end of file From 431f5d76ce67e203af6d000b80cd8393ea25b233 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Fri, 27 Nov 2020 16:12:40 +0000 Subject: [PATCH 019/248] Translated using Weblate (Albanian) Currently translated at 95.2% (200 of 210 strings) Translation: Element Android/Element Android Sdk Translate-URL: https://translate.element.io/projects/element-android/element-sdk/sq/ --- .../src/main/res/values-sq/strings.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/matrix-sdk-android/src/main/res/values-sq/strings.xml b/matrix-sdk-android/src/main/res/values-sq/strings.xml index 4055b35025..6387ebb3bd 100644 --- a/matrix-sdk-android/src/main/res/values-sq/strings.xml +++ b/matrix-sdk-android/src/main/res/values-sq/strings.xml @@ -213,4 +213,17 @@ %1$s erdhi Krijuat diskutimin %1$s krijoi diskutimin + Dhomë e zbrazët (was %s) + + %1$s, %2$s, %3$s dhe %4$d tjetër + %1$s, %2$s, %3$s dhe %4$d të tjerë + + %1$s, %2$s, %3$s dhe %4$s + %1$s, %2$s dhe %3$s + 🎉 U është penguar pjesëmarrja krejt shërbyesve! Kjo dhomë s’mund të përdoret më. + Pa ndryshim. + Ndryshuat ACL-ra shërbyesi për këtë dhomë. + %s ndryshoi ACL-ra shërbyesi për këtë dhomë. + Ujdisët ACL-ra shërbyesi për këtë dhomë. + %s ujdisi ACL-ra shërbyesi për këtë dhomë. \ No newline at end of file From f14b3908498366ad7532f0cb8568d888ab621b2f Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Fri, 27 Nov 2020 16:30:27 +0000 Subject: [PATCH 020/248] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (4 of 4 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/pt_BR/ --- fastlane/metadata/android/pt_BR/changelogs/40100100.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fastlane/metadata/android/pt_BR/changelogs/40100100.txt b/fastlane/metadata/android/pt_BR/changelogs/40100100.txt index 02cfd45a87..4884d7f62a 100644 --- a/fastlane/metadata/android/pt_BR/changelogs/40100100.txt +++ b/fastlane/metadata/android/pt_BR/changelogs/40100100.txt @@ -1 +1,2 @@ -// A FAZER +Esta nova versão contém principalmente correções de erros e melhorias. Enviar mensagens agora é muito mais rápido. +Registro de todas as alterações: https://github.com/vector-im/element-android/releases/tag/v1.0.10 From f6cc05634ff3852be95b20a35c783bb1c0e59660 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 27 Nov 2020 16:38:46 +0100 Subject: [PATCH 021/248] Send task: small rework and cleanup --- .../room/send/queue/EventSenderProcessor.kt | 6 +---- .../session/room/send/queue/QueueMemento.kt | 1 - .../session/room/send/queue/QueuedTask.kt | 12 +++++++--- .../room/send/queue/RedactQueuedTask.kt | 22 +++++++------------ .../room/send/queue/SendEventQueuedTask.kt | 10 ++------- 5 files changed, 20 insertions(+), 31 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt index 62e225c624..b8f6e52674 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt @@ -106,11 +106,7 @@ internal class EventSenderProcessor @Inject constructor( // non blocking add to queue sendingQueue.add(task) markAsManaged(task) - return object : Cancelable { - override fun cancel() { - task.cancel() - } - } + return task } companion object { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt index e69c65ec4c..dfbac347d9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt @@ -17,7 +17,6 @@ package org.matrix.android.sdk.internal.session.room.send.queue import android.content.Context -import org.matrix.android.sdk.api.auth.data.sessionId import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.room.send.SendState diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt index bccbc97ff4..e5c1cf7435 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt @@ -16,14 +16,20 @@ package org.matrix.android.sdk.internal.session.room.send.queue -abstract class QueuedTask { +import org.matrix.android.sdk.api.util.Cancelable + +abstract class QueuedTask : Cancelable { var retryCount = 0 + private var hasBeenCancelled: Boolean = false + abstract suspend fun execute() abstract fun onTaskFailed() - abstract fun isCancelled() : Boolean + open fun isCancelled() = hasBeenCancelled - abstract fun cancel() + final override fun cancel() { + hasBeenCancelled = true + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt index a3c19a1f7c..e2fb978cd0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt @@ -22,18 +22,16 @@ import org.matrix.android.sdk.internal.session.room.send.CancelSendTracker import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository internal class RedactQueuedTask( - val toRedactEventId: String, + private val toRedactEventId: String, val redactionLocalEchoId: String, - val roomId: String, - val reason: String?, - val redactEventTask: RedactEventTask, - val localEchoRepository: LocalEchoRepository, - val cancelSendTracker: CancelSendTracker + private val roomId: String, + private val reason: String?, + private val redactEventTask: RedactEventTask, + private val localEchoRepository: LocalEchoRepository, + private val cancelSendTracker: CancelSendTracker ) : QueuedTask() { - private var _isCancelled: Boolean = false - - override fun toString() = "[RedactEventRunnableTask $redactionLocalEchoId]" + override fun toString() = "[RedactQueuedTask $redactionLocalEchoId]" override suspend fun execute() { redactEventTask.execute(RedactEventTask.Params(redactionLocalEchoId, roomId, toRedactEventId, reason)) @@ -44,10 +42,6 @@ internal class RedactQueuedTask( } override fun isCancelled(): Boolean { - return _isCancelled || cancelSendTracker.isCancelRequestedFor(redactionLocalEchoId, roomId) - } - - override fun cancel() { - _isCancelled = true + return super.isCancelled() || cancelSendTracker.isCancelRequestedFor(redactionLocalEchoId, roomId) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt index 21a4145a9d..f934aad67b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt @@ -33,9 +33,7 @@ internal class SendEventQueuedTask( val cancelSendTracker: CancelSendTracker ) : QueuedTask() { - private var _isCancelled: Boolean = false - - override fun toString() = "[SendEventRunnableTask ${event.eventId}]" + override fun toString() = "[SendEventQueuedTask ${event.eventId}]" override suspend fun execute() { sendEventTask.execute(SendEventTask.Params(event, encrypt)) @@ -56,10 +54,6 @@ internal class SendEventQueuedTask( } override fun isCancelled(): Boolean { - return _isCancelled || cancelSendTracker.isCancelRequestedFor(event.eventId, event.roomId) - } - - override fun cancel() { - _isCancelled = true + return super.isCancelled() || cancelSendTracker.isCancelRequestedFor(event.eventId, event.roomId) } } From cd983de058557ae0bd9902138d80f863dfa78323 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 30 Nov 2020 10:08:31 +0100 Subject: [PATCH 022/248] Fix cancellation of sending event (#2438) --- CHANGES.md | 2 +- .../session/room/send/DefaultSendService.kt | 2 ++ .../room/send/queue/EventSenderProcessor.kt | 14 ++++++++++++++ .../internal/session/room/send/queue/QueuedTask.kt | 8 +++++++- .../session/room/send/queue/RedactQueuedTask.kt | 2 +- .../session/room/send/queue/SendEventQueuedTask.kt | 2 +- 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e48281081b..7f626da497 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,7 +8,7 @@ Improvements 🙌: - Bugfix 🐛: - - + - Fix cancellation of sending event (#2438) Translations 🗣: - diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index b13ce15da6..5a71ff7b76 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -210,6 +210,8 @@ internal class DefaultSendService @AssistedInject constructor( override fun cancelSend(eventId: String) { cancelSendTracker.markLocalEchoForCancel(eventId, roomId) + // This is maybe the current task, so cancel it too + eventSenderProcessor.cancel(eventId, roomId) taskExecutor.executorScope.launch { localEchoRepository.deleteFailedEcho(roomId, eventId) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt index b8f6e52674..5014d94558 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.room.send.queue +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.matrix.android.sdk.api.auth.data.SessionParams @@ -109,10 +110,18 @@ internal class EventSenderProcessor @Inject constructor( return task } + fun cancel(eventId: String, roomId: String) { + (currentTask as? SendEventQueuedTask) + ?.takeIf { it -> it.event.eventId == eventId && it.event.roomId == roomId } + ?.cancel() + } + companion object { private const val RETRY_WAIT_TIME_MS = 10_000L } + private var currentTask: QueuedTask? = null + private var sendingQueue = LinkedBlockingQueue() private var networkAvailableLock = Object() @@ -125,6 +134,7 @@ internal class EventSenderProcessor @Inject constructor( while (!isInterrupted) { Timber.v("## SendThread wait for task to process") val task = sendingQueue.take() + .also { currentTask = it } Timber.v("## SendThread Found task to process $task") if (task.isCancelled()) { @@ -179,6 +189,10 @@ internal class EventSenderProcessor @Inject constructor( task.onTaskFailed() throw InterruptedException() } + exception is CancellationException -> { + Timber.v("## SendThread task has been cancelled") + break@retryLoop + } else -> { Timber.v("## SendThread retryLoop Un-Retryable error, try next task") // this task is in error, check next one? diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt index e5c1cf7435..9a7fcd8d91 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt @@ -23,7 +23,13 @@ abstract class QueuedTask : Cancelable { private var hasBeenCancelled: Boolean = false - abstract suspend fun execute() + suspend fun execute() { + if (!isCancelled()) { + doExecute() + } + } + + abstract suspend fun doExecute() abstract fun onTaskFailed() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt index e2fb978cd0..8e7ba2f155 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt @@ -33,7 +33,7 @@ internal class RedactQueuedTask( override fun toString() = "[RedactQueuedTask $redactionLocalEchoId]" - override suspend fun execute() { + override suspend fun doExecute() { redactEventTask.execute(RedactEventTask.Params(redactionLocalEchoId, roomId, toRedactEventId, reason)) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt index f934aad67b..ea097082c7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt @@ -35,7 +35,7 @@ internal class SendEventQueuedTask( override fun toString() = "[SendEventQueuedTask ${event.eventId}]" - override suspend fun execute() { + override suspend fun doExecute() { sendEventTask.execute(SendEventTask.Params(event, encrypt)) } From c8a8e0f2da50b5f6808b12adf6d9f9c6f14957a6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 30 Nov 2020 10:54:22 +0100 Subject: [PATCH 023/248] 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 024/248] 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 025/248] 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 026/248] 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 027/248] 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 028/248] 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 029/248] 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 030/248] 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 031/248] 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 032/248] 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 033/248] 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 034/248] 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 035/248] 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 036/248] 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 037/248] 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 038/248] 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 039/248] 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 040/248] 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 041/248] 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 042/248] 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 043/248] 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 044/248] 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 045/248] 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 046/248] 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 047/248] 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 048/248] 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 049/248] 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 050/248] 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 051/248] 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 052/248] 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 053/248] 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 054/248] 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 055/248] 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 056/248] 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 057/248] 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 058/248] 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 059/248] 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 060/248] 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 061/248] 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 062/248] 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 063/248] 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 064/248] 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 065/248] 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 066/248] 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 067/248] 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 068/248] 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 069/248] 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 070/248] 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 071/248] 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 072/248] 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 073/248] 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 7bd8d54d5c1aec326c6b411e0efa4f67451e2192 Mon Sep 17 00:00:00 2001 From: Danial Behzadi Date: Mon, 30 Nov 2020 03:08:55 +0000 Subject: [PATCH 074/248] Translated using Weblate (Persian) Currently translated at 99.4% (1976 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/fa/ --- vector/src/main/res/values-fa/strings.xml | 74 ++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values-fa/strings.xml b/vector/src/main/res/values-fa/strings.xml index ae3292bb2c..99b86327d9 100644 --- a/vector/src/main/res/values-fa/strings.xml +++ b/vector/src/main/res/values-fa/strings.xml @@ -1699,7 +1699,7 @@ برای امنیت بیشتر، %s را با بررسی یک کد یکبارمصرف تایید کنید. پس از فعال‌شدن ، نمی‌توان رمزگذاری برای یک اتاق را غیرفعال کرد. پیام های ارسالی در یک اتاق رمزگذاری شده توسط سرور قابل مشاهده نیست و فقط توسط اعضای اتاق قابل مشاهده است. فعال کردن رمزنگاری ممکن است از کارکرد بسیاری از ربات ها و پل ها جلوگیری کند. رمزگذاری فعال شود؟ - فعال‌سازی رمزنگاری سرتاسر + به کار انداختن رمزنگاری سرتاسری… شکلک را با رنگ‌بندی رنگین گمان ارسال می کند پیام را با رنگ‌بندی رنگین کمان ارسال می کند این نشست نمی‌تواند تائید را با نشست‌های دیگر شما به اشتراک بگذارد. @@ -2159,4 +2159,76 @@ پیام مستقیم ارسال تاریخچهٔ درخواست‌های هم‌رسانی کلید نتایج بیش‌تری نیست + کلید امنیتیتان را در جایی امن، مانند مدیر گذرواژه یا گاوصندوق نگه دارید. + عبارت امنیتی‌ای را که تنها خودتان می‌دانید، برای امن کردن رمزها روی کارسازتان وارد کنید. + کلید امنیتیتان را در جایی امن، مانند مدیر گذرواژه یا گاوصندوق نگه دارید. + عبارت رمزی که فقط خودتان می‌دانید را وارد کرده و کلیدی برای پشتیبان تولید کنید. + به هنوتم جایگزین، می‌توانید نشامی هر کارساز هویت دیگری را وارد کنید + کارساز خانگیتان (%1$s) پیشنهاد استفاده از %2$s برای کارساز هویتتان را می‌دهد + برای محرمانگیتان، المنت تنها از فرستادن شماره تلفن و رایانامه‌های کاربری در هم ریخته پشتیبانی می‌کند. + این عملیات ممکن نیست. کارساز خانگی منقضی شده است. + نتوانستیم کاربران را دعوت کنیم. لطفاً کاربرانی که می‌خواهید دعوت کنید را بررسی کرده و دوباره تلاش کنید. + نتوانستیم پیامتان را ایجاد کنیم. لطفاً کاربرانی که می‌خواهید دعوت کنید را بررسی کرده و دوباره تلاش کنید. + انسداد هرکسی خارج از %s از پیوستن به این اتاق + پیوند ماتریکس + دور ریختن تغییرات + تغییرات ذخیره‌نشده‌ای وجود دارد. دور ریختن تغییرات؟ + اتاق هنوز ایجاد نشده است. لغو ایجاد اتاق؟ + رمز QR پویش نشد! + رمز QR نامعتبر (نشانی نامعتبر)! + نمی‌توانید به خودتان پیام دهید! + هم‌رسانی با متن + جست‌وجوی مخاطبان در ماتریکس + تنظیم آواتار + ذخیرهٔ کلید امنیتیتان + برای تأیید عبارت امنیتیتان، دوباره واردش کنید. + عبارت امنیتی + تنظیم یک عبارت امنیتی + ذخیرهٔ کلید امنیتیتان + استفاده از یک عبارت امنیتی + نشانی کارساز هویتی را وارد کنید + رضایت کاربر فراهم نشده است. + لطفاً نخست شرایط کارساز هویت را در تنظیمات بپذیرید. + لطفاً نخست کارساز هویتی را پیکربندی کنید. + کارساز هویت منقضی شده اسن. المنت تنها نگارش ۲ از API را پشتیبانی می‌کند. + این رمز را افراد هم‌رسانی کرده تا بتوانند برای افزودنتان و شروع گپ، بپویندش. + رمزم + هم‌رسانی رمزم + پویش یک رمز QR + این یک رمز QR ماتریکس معتبر نیست + 🔐️ پیوستن به من روی المنت + سلام. روی المنت باهام حرف بزن: %s + دعوت دوستان + افزودن افراد + "موضوع: " + افزودن موضوع + %s برای دادن اجازهٔ دانستن موضوع این اتاق به افراد. + این، آغاز تاریخچهٔ پیام مستقیمتان با %s است. + این، آغاز %s است. + این، آغاز گفت‌وگوست. + اجازهٔ به کار انداختن رمزنگاری در این اتاق را ندارید. + ایجاد کردن اتاق… + برخی نویسه‌ها مجاز نیستند + لطفاً نام اتاقی را وارد کنید + این نشانی در حال استفاده است + نشانی اتاق + نهفتن پیشرفته + نمایش پیشرفته + %1$d از %2$d + ایجاد گفت‌وگوی مسنقیمی جدید با پویش یک رمز QR + ایجاد گفت‌وگوی مستقیمی جدید با شناسهٔ ماتریکس + فرستادن رایانامه‌ها و شماره تلفن‌ها + رضایت دادن + ابطال رضایتم + فرستادن رایانامه‌ها و شماره تلفن‌ها + پیشنهادها + مخاطبان + کاربران شناخته + اخیر + رمز QR + افزودن با رمز QR + جست‌وجو با نام یا شناسه + دسترسی به مخاطبانتان را مجاز کنید. + برای پویش یک رمز QR نیاز است دسترسی به دوربین را مجاز کنید. + آغاز به گپ \ No newline at end of file From b53ba2b98e7913c004b66a9d8d72f6e43d381936 Mon Sep 17 00:00:00 2001 From: Kaede Date: Mon, 30 Nov 2020 01:35:50 +0000 Subject: [PATCH 075/248] Translated using Weblate (Japanese) Currently translated at 49.8% (991 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/ja/ --- vector/src/main/res/values-ja/strings.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/vector/src/main/res/values-ja/strings.xml b/vector/src/main/res/values-ja/strings.xml index e764f5001b..45bb33c807 100644 --- a/vector/src/main/res/values-ja/strings.xml +++ b/vector/src/main/res/values-ja/strings.xml @@ -1098,4 +1098,18 @@ Matrixでのメッセージの可視性は電子メールと同様です。メ グループ通話を開始する権限がありません リセット なし + トピック + 部屋名 + この部屋内のメッセージはエンドツーエンド暗号化されていません。 + ここでのメッセージはエンドツーエンド暗号化されていません。 + 設定 + あなたにはこの部屋の暗号化を有効にする権限がありません。 + 未読メッセージ + タイムラインでスワイプして返信を有効にする + タイムラインで非表示のイベントを表示する + QR コードをスキャン + QR コード画像 + QR コード + QR コードによる追加 + コードを共有 \ No newline at end of file From e42906f08ad3171ed6eadd1227520c688a2019dc Mon Sep 17 00:00:00 2001 From: Nikita Epifanov Date: Mon, 30 Nov 2020 12:41:04 +0000 Subject: [PATCH 076/248] Translated using Weblate (Russian) Currently translated at 99.3% (1973 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/ru/ --- vector/src/main/res/values-ru/strings.xml | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/vector/src/main/res/values-ru/strings.xml b/vector/src/main/res/values-ru/strings.xml index 1064748828..2974a3374b 100644 --- a/vector/src/main/res/values-ru/strings.xml +++ b/vector/src/main/res/values-ru/strings.xml @@ -2306,4 +2306,49 @@ Поиск по имени или идентификатору Разрешить доступ к вашим контактам. Чтобы отсканировать QR-код, вам нужно разрешить доступ к камере. + Ссылка Matrix + QR-код не отсканирован! + Недействительный QR-код (недопустимый URI)! + Нельзя отправлять сообщения самому себе! + Поделиться по тексту + Поиск контактов в Matrix + Установить аватар + Согласие пользователя не предоставлено. + Поделитесь этим кодом с людьми, чтобы они могли отсканировать его, добавить вас и начать общение. + Мой код + Поделиться моим кодом + Сканировать QR-код + Это недействительный QR-код matrix + 🔐️ Присоединяйтесь ко мне в Element + Привет, поговори со мной в Element: %s + Пригласить друзей + Добавить людей + "Тема: " + Добавить тему + Это начало разговора. + Это начало %s. + У вас нет разрешения на включение шифрования в этой комнате. + Создание комнаты… + Некоторые символы не разрешены + Укажите адрес комнаты + Этот адрес уже используется + Адрес комнаты + Вы можете включить это, если комната будет использоваться только для совместной работы с внутренними командами на вашем домашнем сервере. Это не может быть изменено позже. + Запретить кому-либо, не входящему в %s, когда-либо присоединяться к этой комнате + Скрыть дополнительные настройки + Показать дополнительные настройки + %1$d из %2$d + Согласны ли вы на отправку своих контактных данных (номера телефонов и/или электронную почту) на настроенный сервер идентификации (%1$s) для обнаружения контактов\? +\n +\nДля большей конфиденциальности отправленные данные перед отправкой будут хешированы. + Дать согласие + Отозвать моё согласие + Вы не дали свое согласие на отправку электронной почты и номеров телефонов на этот сервер идентификации для обнаружения других пользователей из ваших контактов. + Больше никаких результатов + Предложения + Контакты + Известные пользователи + QR-код + Отправить историю запросов на обмен ключами + Начать беседу \ No newline at end of file From 6236b01189aaf28cad8dea44dc1fafb9ec0ab609 Mon Sep 17 00:00:00 2001 From: HolgerHuo Date: Mon, 30 Nov 2020 12:00:52 +0000 Subject: [PATCH 077/248] Translated using Weblate (Chinese (Simplified)) Currently translated at 96.7% (1921 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hans/ --- vector/src/main/res/values-zh-rCN/strings.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values-zh-rCN/strings.xml b/vector/src/main/res/values-zh-rCN/strings.xml index bcb449cabb..db7a1c4b89 100644 --- a/vector/src/main/res/values-zh-rCN/strings.xml +++ b/vector/src/main/res/values-zh-rCN/strings.xml @@ -702,7 +702,7 @@ 请允许资料分析以帮助我们改进 Element。 是的,我愿意帮助! 停用账户 - + 请在我停用账户的同时忘记我发送的所有消息(警告:这将导致未来的用户看到残缺的对话) 请输入您的密码以继续: 停用账户 @@ -2148,4 +2148,8 @@ 您没有权限发起通话 您没有权限发起会议通话 重置 + 允许访问您的联系人。 + 如需扫描二维码,您须允许相机访问权限。 + 没有更多结果 + 开始畅聊 \ No newline at end of file From 1c3a279b8acee7d5cb7372635de4059adc3611fd Mon Sep 17 00:00:00 2001 From: sr093906 Date: Mon, 30 Nov 2020 09:14:04 +0000 Subject: [PATCH 078/248] Translated using Weblate (Chinese (Simplified)) Currently translated at 96.7% (1921 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hans/ --- vector/src/main/res/values-zh-rCN/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/values-zh-rCN/strings.xml b/vector/src/main/res/values-zh-rCN/strings.xml index db7a1c4b89..4c89c5a26f 100644 --- a/vector/src/main/res/values-zh-rCN/strings.xml +++ b/vector/src/main/res/values-zh-rCN/strings.xml @@ -1723,7 +1723,7 @@ 发送彩虹色给定表情 时间线 消息编辑器 - 启用端到端加密 + 启用端到端加密… 一旦启用,加密无法禁用。 是否启用加密? 启用后,将无法禁用聊天室加密。服务器无法看到加密聊天室中发送的消息,只有聊天室的参与者才能看到。启用加密可能会阻止许多机器人和桥接正常工作。 From 3b21400bb85306bfafd98bebdb99c1c4a5231af5 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Mon, 30 Nov 2020 02:32:10 +0000 Subject: [PATCH 079/248] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (1986 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hant/ --- vector/src/main/res/values-zh-rTW/strings.xml | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values-zh-rTW/strings.xml b/vector/src/main/res/values-zh-rTW/strings.xml index 8d019a7635..d39254d506 100644 --- a/vector/src/main/res/values-zh-rTW/strings.xml +++ b/vector/src/main/res/values-zh-rTW/strings.xml @@ -1623,7 +1623,7 @@ 將指定的表情符號以彩虹的方式上色後傳送 時間軸 訊息編輯器 - 啟用端到端加密 + 啟用端到端加密…… 一旦啟用加密就無法停用。 啟用加密? 一旦啟用對聊天室的加密就無法停用。傳送到已加密聊天室的訊息無法被伺服器看見,僅有聊天室的參與者可見。啟用加密可能會讓許多機器人與橋接無法運作。 @@ -2153,4 +2153,59 @@ 直接訊息 傳送金鑰共享請求歷史 沒有更多結果 + Matrix 連結 + 未掃描 QR code! + 無效的 QR code(無效的 URI)! + 無法對您自己直接訊息! + 透過文字分享 + 在 Matrix 上搜尋聯絡人 + 設定大頭照 + 使用者尚未提供其同意。 + 與夥伴們分享此條碼,這樣他們就可以掃描它來加入您並開始聊天。 + 我的條碼 + 分享我的條碼 + 掃描 QR code + 不是有效的 Matrix QR code + 🔐️ 在 Element 上加入我 + 嗨,和我在 Element 上聊天吧:%s + 邀請朋友 + "主題: " + 新增夥伴 + 新增主題 + %s 讓人們知道這個聊天室是做什麼用的。 + 這是您與 %s 直接訊息歷史紀錄的開頭。 + 這是此對話的開頭。 + 這是 %s 的開頭。 + 您沒有在此聊天室中啟用加密的權限。 + 正在建立聊天室…… + 不允許部份字元 + 請提供聊天室地址 + 此地址已被使用 + 聊天室地址 + 如果聊天室僅用於與您的家伺服氣上的內部團隊協作的話,可以啟用此功能。但無法在稍後變更。 + 封鎖任何不是 %s 一部分的人加入此聊天室 + 隱藏進階 + 顯示進階 + %2$d 中的 %1$d + 透過掃描 QR code 建立新的直接對話 + 透過 Matrix ID 建立新的直接對話 + 為了探索您已知的既有聯絡人,您是否接受將您的聯絡人資料(電話號碼與/或電子郵件)傳送到設定好的身份伺服器 (%1$s)? +\n +\n為了更好的隱私,要傳送的資料將會先雜湊過再傳送。 + 傳送電子郵件與電話號碼 + 給予同意 + 撤銷我的同意 + 您尚未同意傳送電子郵件與電話號碼到此身份提供者以從您的聯絡人中探索其他使用者。 + 您已同意傳送電子郵件與電話號碼到此身份提供者以從您的聯絡人中探索其他使用者。 + 傳送電子郵件或電話號碼 + 建議 + 聯絡人 + 已知的使用者 + 最近 + QR code + 透過 QR code 新增 + 使用名稱或 ID 搜尋 + 允許存取您聯絡人的權限。 + 要掃描 QR code,您必須允許存取攝影機。 + 開始聊天 \ No newline at end of file From 6d0f9baba4907bd166b4654f555dd50fef05f2ee Mon Sep 17 00:00:00 2001 From: Nikita Epifanov Date: Mon, 30 Nov 2020 12:49:10 +0000 Subject: [PATCH 080/248] Translated using Weblate (Russian) Currently translated at 92.8% (195 of 210 strings) Translation: Element Android/Element Android Sdk Translate-URL: https://translate.element.io/projects/element-android/element-sdk/ru/ --- .../src/main/res/values-ru/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/matrix-sdk-android/src/main/res/values-ru/strings.xml b/matrix-sdk-android/src/main/res/values-ru/strings.xml index ef9cda1dc2..84a3ac3ec3 100644 --- a/matrix-sdk-android/src/main/res/values-ru/strings.xml +++ b/matrix-sdk-android/src/main/res/values-ru/strings.xml @@ -227,4 +227,16 @@ %1$s создал(а) обсуждение Вы обновили. %s обновлена. + + %1$s, %2$s, %3$s и %4$d другой + %1$s, %2$s, %3$s и %4$d других + %1$s, %2$s, %3$s и %4$d другие + %1$s, %2$s, %3$s и %4$d другие + + %1$s, %2$s, %3$s и %4$s + %1$s, %2$s и %3$s + 🎉 Всем серверам запрещено участвовать! Эта комната больше не может быть использована. + Без изменений. + Пустая комната (была %s) + • Соответствующий сервер %s заблокирован. \ No newline at end of file From 088608011b0f1e77773d40eeb2b246c8e6778480 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Mon, 30 Nov 2020 02:47:25 +0000 Subject: [PATCH 081/248] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (210 of 210 strings) Translation: Element Android/Element Android Sdk Translate-URL: https://translate.element.io/projects/element-android/element-sdk/zh_Hant/ --- .../src/main/res/values-zh-rTW/strings.xml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml b/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml index b3de5910a5..08050b400d 100644 --- a/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml +++ b/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml @@ -208,4 +208,26 @@ %1$s 已加入 您已建立此討論 %1$s 已建立此討論 + 空的聊天室(曾為 %s) + + %1$s, %2$s, %3$s 與 %4$d 個其他 + + %1$s, %2$s, %3$s 與 %4$s + %1$s, %2$s 與 %3$s + 🎉 禁止所有伺服器參與!無法再使用此聊天室。 + 無變更。 + • 禁止伺服器符合 IP 文字。 + • 允許伺服器符合 IP 文字。 + • 伺服器符合 %s 已從允許清單中移除。 + • 允許伺服器符合 %s。 + • 伺服器符合 %s 已從禁止清單中移除。 + • 現在禁止伺服器符合 %s。 + 您為此聊天室變更了伺服器 ACL。 + %s 為此聊天是變更了伺服器 ACL。 + • 禁止伺服器符合 IP 文字。 + • 允許伺服器符合 IP 文字。 + • 已允許伺服器符合 %s。 + • 已禁止伺服器符合 %s。 + 您為此聊天是設定了伺服器 ACL。 + %s 為此聊天是設定了伺服器 ACL。 \ No newline at end of file From 313393506378b362e953f95a0817240cd7bc0c50 Mon Sep 17 00:00:00 2001 From: strix aluco Date: Mon, 30 Nov 2020 05:34:25 +0000 Subject: [PATCH 082/248] Translated using Weblate (Ukrainian) Currently translated at 100.0% (4 of 4 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/uk/ --- fastlane/metadata/android/uk/changelogs/40100100.txt | 2 ++ fastlane/metadata/android/uk/full_description.txt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 fastlane/metadata/android/uk/changelogs/40100100.txt diff --git a/fastlane/metadata/android/uk/changelogs/40100100.txt b/fastlane/metadata/android/uk/changelogs/40100100.txt new file mode 100644 index 0000000000..e5333ae561 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40100100.txt @@ -0,0 +1,2 @@ +Ця версія містить переважно виправлення помилок та деякі покращення. Відправлення повідомлень стало тепер ще швидшим. +Повний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.0.10 diff --git a/fastlane/metadata/android/uk/full_description.txt b/fastlane/metadata/android/uk/full_description.txt index 64247581d2..026ae4162a 100644 --- a/fastlane/metadata/android/uk/full_description.txt +++ b/fastlane/metadata/android/uk/full_description.txt @@ -7,7 +7,7 @@ Element — це застосунок для спілкування та спі Element ґрунтовно відрізняється від інших застосунків для спілкування та співпраці тому що він є децентралізованим та відкритоджерельним. -Element дозволяє вам розміщувати сервер в себе або обирати будь-якого з надавачів послуг, таким чином забезпечуючи вам конфіденційність і можливість володіти власними даними й бесідами та контролювати їх. Він надає вам доступ до відкритої мережі, тож ви не є обмеженими спілкуванням виключно з користувачами Element. І він є дуже надійним та безпечним. +Element дозволяє вам розміщувати сервер в себе або обирати будь-якого з надавачів послуг, таким чином забезпечуючи вам конфіденційність і можливість володіти власними даними й бесідами та контролювати їх. Він надає вам доступ до відкритої мережі, тож ви не є обмеженими спілкуванням виключно з користувачами Element. І він є дуже надійним та безпечним. Element здатен забезпечити усе це завдяки тому, що він заснований на протоколі Matrix — стандарті для відкритого та децентралізованого спілкування. From 04914be442845798712b97dd4a2ef7d6ee0a147a Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Mon, 30 Nov 2020 02:12:17 +0000 Subject: [PATCH 083/248] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (4 of 4 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/zh_Hant/ --- fastlane/metadata/android/zh_Hant/changelogs/40100100.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fastlane/metadata/android/zh_Hant/changelogs/40100100.txt b/fastlane/metadata/android/zh_Hant/changelogs/40100100.txt index 3c21bcbeb6..0ea092ba9a 100644 --- a/fastlane/metadata/android/zh_Hant/changelogs/40100100.txt +++ b/fastlane/metadata/android/zh_Hant/changelogs/40100100.txt @@ -1 +1,2 @@ -// 待辦事項 +這個新版本主要包含錯誤修復與改善。傳送訊息更快了。 +完整的變更紀錄請見:https://github.com/vector-im/element-android/releases/tag/v1.0.10 From c6ba2960287d51344b469be9529dc39e165b7a78 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 1 Dec 2020 14:48:10 +0100 Subject: [PATCH 084/248] 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 085/248] 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 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/theme_dark.xml b/vector/src/main/res/values/theme_dark.xml index 6ebf8e2b9b..6be0adb907 100644 --- a/vector/src/main/res/values/theme_dark.xml +++ b/vector/src/main/res/values/theme_dark.xml @@ -194,6 +194,12 @@ @transition/image_preview_transition @transition/image_preview_transition + + @style/WidgetButtonSocialLogin.Google.Dark + @style/WidgetButtonSocialLogin.Github.Dark + @style/WidgetButtonSocialLogin.Facebook.Dark + @style/WidgetButtonSocialLogin.Twitter.Dark + @style/WidgetButtonSocialLogin.Apple.Dark - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/vector/src/main/res/values/styles_social_login.xml b/vector/src/main/res/values/styles_social_login.xml new file mode 100644 index 0000000000..796965cee1 --- /dev/null +++ b/vector/src/main/res/values/styles_social_login.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 09040b70954efc20e5b28e80af9939945bca2bfa Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Dec 2020 16:20:36 +0100 Subject: [PATCH 236/248] Clear history (#1933) --- CHANGES.md | 1 + .../org/matrix/android/sdk/internal/raw/RawModule.kt | 3 +-- .../java/im/vector/app/features/login/LoginAction.kt | 3 +++ .../app/features/login/LoginServerUrlFormFragment.kt | 11 ++++++++++- .../im/vector/app/features/login/LoginViewModel.kt | 12 +++++++----- .../res/layout/fragment_login_server_url_form.xml | 11 +++++++++++ vector/src/main/res/values/strings.xml | 1 + 7 files changed, 34 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 78cea417aa..62bd92006e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,7 @@ Improvements 🙌: - Add Setting Item to Change PIN (#2462) - Improve room history visibility setting UX (#1579) - Matrix.to deeplink custom scheme support + - Homeserver history (#1933) Bugfix 🐛: - Fix cancellation of sending event (#2438) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawModule.kt index 6ed14ddb3c..50721b809a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawModule.kt @@ -24,9 +24,7 @@ import dagger.Module import dagger.Provides import io.realm.RealmConfiguration import okhttp3.OkHttpClient -import org.matrix.android.sdk.api.auth.HomeServerHistoryService import org.matrix.android.sdk.api.raw.RawService -import org.matrix.android.sdk.internal.auth.DefaultHomeServerHistoryService import org.matrix.android.sdk.internal.database.RealmKeysUtils import org.matrix.android.sdk.internal.di.GlobalDatabase import org.matrix.android.sdk.internal.di.MatrixScope @@ -61,6 +59,7 @@ internal abstract class RawModule { .name("matrix-sdk-global.realm") .schemaVersion(GlobalRealmMigration.SCHEMA_VERSION) .migration(GlobalRealmMigration) + .allowWritesOnUiThread(true) .modules(GlobalRealmModule()) .build() } diff --git a/vector/src/main/java/im/vector/app/features/login/LoginAction.kt b/vector/src/main/java/im/vector/app/features/login/LoginAction.kt index 9ab02711b5..2b4e3d6be0 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginAction.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginAction.kt @@ -60,6 +60,9 @@ sealed class LoginAction : VectorViewModelAction { object ResetLogin : ResetAction() object ResetResetPassword : ResetAction() + // Homeserver history + object ClearHomeServerHistory : LoginAction() + // For the soft logout case data class SetupSsoForSessionRecovery(val homeServerUrl: String, val deviceId: String, diff --git a/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt index 716c1cda88..1915cdd204 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt @@ -21,10 +21,12 @@ import android.os.Bundle import android.view.View import android.view.inputmethod.EditorInfo import android.widget.ArrayAdapter +import androidx.core.view.isInvisible import androidx.core.view.isVisible import butterknife.OnClick import com.google.android.material.textfield.TextInputLayout import com.jakewharton.rxbinding3.widget.textChanges +import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.utils.ensureProtocol @@ -84,7 +86,7 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment() loginServerUrlFormNotice.text = getString(R.string.login_server_url_form_common_notice) } } - val completions = state.knownCustomHomeServersUrls + val completions = state.knownCustomHomeServersUrls + if (BuildConfig.DEBUG) listOf("http://10.0.2.2:8080") else emptyList() loginServerUrlFormHomeServerUrl.setAdapter(ArrayAdapter( requireContext(), R.layout.item_completion_homeserver, @@ -100,6 +102,11 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment() openUrlInChromeCustomTab(requireActivity(), null, EMS_LINK) } + @OnClick(R.id.loginServerUrlFormClearHistory) + fun clearHistory() { + loginViewModel.handle(LoginAction.ClearHomeServerHistory) + } + override fun resetViewModel() { loginViewModel.handle(LoginAction.ResetHomeServerUrl) } @@ -141,6 +148,8 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment() override fun updateWithState(state: LoginViewState) { setupUi(state) + loginServerUrlFormClearHistory.isInvisible = state.knownCustomHomeServersUrls.isEmpty() + if (state.loginMode != LoginMode.Unknown) { // The home server url is valid loginViewModel.handle(LoginAction.PostViewEvent(LoginViewEvents.OnLoginFlowRetrieved)) diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt index 2f84c3081d..0a6dbcaae2 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt @@ -28,7 +28,6 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.extensions.configureAndStart @@ -80,10 +79,7 @@ class LoginViewModel @AssistedInject constructor( private fun getKnownCustomHomeServersUrls() { setState { - copy( - knownCustomHomeServersUrls = homeServerHistoryService.getKnownServersUrls() - + if (BuildConfig.DEBUG) listOf("http://10.0.2.2:8080") else emptyList() - ) + copy(knownCustomHomeServersUrls = homeServerHistoryService.getKnownServersUrls()) } } @@ -137,6 +133,7 @@ class LoginViewModel @AssistedInject constructor( is LoginAction.ResetAction -> handleResetAction(action) is LoginAction.SetupSsoForSessionRecovery -> handleSetupSsoForSessionRecovery(action) is LoginAction.UserAcceptCertificate -> handleUserAcceptCertificate(action) + LoginAction.ClearHomeServerHistory -> handleClearHomeServerHistory() is LoginAction.PostViewEvent -> _viewEvents.post(action.viewEvent) }.exhaustive } @@ -167,6 +164,11 @@ class LoginViewModel @AssistedInject constructor( getKnownCustomHomeServersUrls() } + private fun handleClearHomeServerHistory() { + homeServerHistoryService.clearHistory() + getKnownCustomHomeServersUrls() + } + private fun handleLoginWithToken(action: LoginAction.LoginWithToken) { val safeLoginWizard = loginWizard diff --git a/vector/src/main/res/layout/fragment_login_server_url_form.xml b/vector/src/main/res/layout/fragment_login_server_url_form.xml index 3c223f19cc..a441fee3be 100644 --- a/vector/src/main/res/layout/fragment_login_server_url_form.xml +++ b/vector/src/main/res/layout/fragment_login_server_url_form.xml @@ -70,6 +70,17 @@ + + Sign Up Sign In Continue with SSO + Clear history Element Matrix Services Address Address From 995ec2599025eb398068d8a0ba2a9af45151869a Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Sun, 13 Dec 2020 15:13:58 +0000 Subject: [PATCH 237/248] Translated using Weblate (Albanian) Currently translated at 99.4% (1975 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/sq/ --- vector/src/main/res/values-sq/strings.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml index 93e82a7cde..9be2118399 100644 --- a/vector/src/main/res/values-sq/strings.xml +++ b/vector/src/main/res/values-sq/strings.xml @@ -1099,7 +1099,8 @@ Na ndjeni, thirrjet konferencë me Jitsi-n nuk mbulohen në pajisje të vjetra (pajisje me Android OS nën 5.0) Verifiko sesion ip e panjohur - Një sesion i ri po kërkon emër keys.ession fshehtëzimi: %1$s + Një sesion i ri po kërkon emër kyçe fshehtëzimi. +\nEmër sesioni: %1$s \nParë së fundi më: %2$s \nNëse s’keni bërë hyrje në një tjetër sesion, shpërfilleni këtë kërkesë. Një sesion i paverifikuar po kërkon kyçe fshehtëzimi. @@ -1615,7 +1616,7 @@ E dërgon emote-n e dhënë të ngjyrosur si ylber Rrjedhë kohore Përpunues mesazhesh - Aktivizoni fshehtëzim skaj-më-skaj + Aktivizoni fshehtëzim skaj-më-skaj… Pasi të aktivizohet, fshehtëzimi s’mund të çaktivizohet më. Të aktivizohet fshehtëzimi\? Pasi të aktivizohet, fshehtëzimi për një dhomë nuk mund të çaktivizohet. Mesazhet e dërguar në një dhomë të fshehtëzuar s’mund të shihen nga shërbyesi, vetëm nga pjesëmarrësit te dhoma. Aktivizimi i fshehtëzimit mund të pengojë funksionimin si duhet të mjaft robotëve dhe urave. @@ -2234,4 +2235,5 @@ Kërkoni sipas emri ose ID-je Që të skanoni një kod QR, lypset të lejoni përdorim kamere. Filloni të Llafoseni + Jepni leje për hyrje te kontaktet tuaja. \ No newline at end of file From 545e13c8433f22ddbd8d979669f85a8cdb773506 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Sun, 13 Dec 2020 23:22:42 +0000 Subject: [PATCH 238/248] Translated using Weblate (Ukrainian) Currently translated at 48.0% (955 of 1986 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/ --- vector/src/main/res/values-uk/strings.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vector/src/main/res/values-uk/strings.xml b/vector/src/main/res/values-uk/strings.xml index 186bf81a9b..ff949f88b5 100644 --- a/vector/src/main/res/values-uk/strings.xml +++ b/vector/src/main/res/values-uk/strings.xml @@ -80,11 +80,11 @@ Запрошення Низький пріоритет - Діалоги + Бесіди Локальні контакти Каталог користувачів Лише Matrix-контакти - Немає діалогів + Немає бесід Ви не надали Element доступу до контактів Немає результатів @@ -1107,9 +1107,9 @@ Перевірка… (%1$d з %2$d) Перевірити Сповіщення усунення несправностей - Захистити приватність діалогів шифруванням - Фільтрувати діалоги… - Діалоги + Захистити приватність бесід шифруванням + Фільтрувати бесіди… + Бесіди У вас більше немає непрочитаних повідомлень Все прочитано! Встановти важливість сповіщень за подіями From 6687a74f5c47a7e36ef6eccd98333bf5939020c0 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Sun, 13 Dec 2020 15:28:10 +0000 Subject: [PATCH 239/248] Translated using Weblate (Albanian) Currently translated at 98.0% (206 of 210 strings) Translation: Element Android/Element Android Sdk Translate-URL: https://translate.element.io/projects/element-android/element-sdk/sq/ --- matrix-sdk-android/src/main/res/values-sq/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/matrix-sdk-android/src/main/res/values-sq/strings.xml b/matrix-sdk-android/src/main/res/values-sq/strings.xml index 6387ebb3bd..58ba8877bb 100644 --- a/matrix-sdk-android/src/main/res/values-sq/strings.xml +++ b/matrix-sdk-android/src/main/res/values-sq/strings.xml @@ -226,4 +226,10 @@ %s ndryshoi ACL-ra shërbyesi për këtë dhomë. Ujdisët ACL-ra shërbyesi për këtë dhomë. %s ujdisi ACL-ra shërbyesi për këtë dhomë. + • Shërbyes që kanë përputhje me %s u hoqën nga lista e të lejuarve. + • Shërbyesit që kanë përputhje me %s tani janë të lejuar. + • Shërbyesit që kanë përputhje me %s u hoqën nga lista e ndalimeve. + • Shërbyesit që kanë përputhje me %s tani janë të ndaluar. + • Shërbyesit që kanë përputhje me %s janë të ndaluar. + • Shërbyesit që kanë përputhje me %s janë të ndaluar. \ No newline at end of file From 2d4eeb64c5c3a8feab7370bb01382723f1600deb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Dec 2020 16:28:17 +0000 Subject: [PATCH 240/248] Translated using Weblate (French) Currently translated at 98.2% (1982 of 2018 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/fr/ --- vector/src/main/res/values-fr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/values-fr/strings.xml b/vector/src/main/res/values-fr/strings.xml index a38ba7ceb9..7eb188b070 100644 --- a/vector/src/main/res/values-fr/strings.xml +++ b/vector/src/main/res/values-fr/strings.xml @@ -2200,7 +2200,7 @@ Plus aucun résultat Exporter le rapport d\'audit %s pour permettre aux gens de connaître le sujet de ce salon. - Ceci est le début de l\'historique de vos message direct avec %s. + Ceci est le début de l\'historique de vos messages directs avec %s. Ceci est le début de cette conversation. Ceci est le début de %s. Vous n\'avez pas le droit d\'activer le chiffrement dans ce salon. From 211c158e23d82b0ec0c2313816a252453c7f171c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Dec 2020 17:42:52 +0100 Subject: [PATCH 241/248] Remove bad translation --- matrix-sdk-android/src/main/res/values-fa/strings.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/matrix-sdk-android/src/main/res/values-fa/strings.xml b/matrix-sdk-android/src/main/res/values-fa/strings.xml index 37aa2629a0..8f8059067e 100644 --- a/matrix-sdk-android/src/main/res/values-fa/strings.xml +++ b/matrix-sdk-android/src/main/res/values-fa/strings.xml @@ -217,6 +217,5 @@ %1$s، %2$s، %3$s و %4$d نفر دیگر %1$s، %2$s، %3$s و %4$d نفر دیگر - %1$s، %2$s و %3$s %1$s، %2$s و %3$s \ No newline at end of file From 7da8b13cde95e1ef9c94232237aa0593354ecaef Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 9 Dec 2020 12:34:22 +0100 Subject: [PATCH 242/248] Chat Effects --- CHANGES.md | 1 + build.gradle | 4 + .../session/room/model/message/MessageType.kt | 3 + vector/build.gradle | 3 + .../src/main/assets/open_source_licenses.html | 14 ++ .../im/vector/app/features/command/Command.kt | 3 +- .../app/features/command/CommandParser.kt | 4 + .../app/features/command/ParsedCommand.kt | 1 + .../home/room/detail/ChatEffectManager.kt | 125 ++++++++++++++ .../home/room/detail/RoomDetailFragment.kt | 36 ++++ .../home/room/detail/RoomDetailViewEvents.kt | 3 + .../home/room/detail/RoomDetailViewModel.kt | 159 ++++++++++-------- .../features/settings/VectorPreferences.kt | 5 + .../src/main/res/drawable-anydpi-v26/snow.png | Bin 0 -> 1986 bytes vector/src/main/res/drawable/ic_snow.xml | 13 ++ .../main/res/layout/fragment_room_detail.xml | 15 ++ vector/src/main/res/values/attrs.xml | 1 + vector/src/main/res/values/strings.xml | 4 + vector/src/main/res/values/theme_dark.xml | 3 + vector/src/main/res/values/theme_light.xml | 4 +- .../res/xml/vector_settings_preferences.xml | 6 + 21 files changed, 339 insertions(+), 68 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/ChatEffectManager.kt create mode 100644 vector/src/main/res/drawable-anydpi-v26/snow.png create mode 100644 vector/src/main/res/drawable/ic_snow.xml diff --git a/CHANGES.md b/CHANGES.md index 62bd92006e..d18a18e1c3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ Features ✨: - Store encrypted file in cache and cleanup decrypted file at each app start (#2512) - Emoji Keyboard (#2520) - Social login (#2452) + - Support for chat effects in timeline (confetti, snow) (#2535) Improvements 🙌: - Add Setting Item to Change PIN (#2462) diff --git a/build.gradle b/build.gradle index 6dd61a720c..7531dee61e 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,10 @@ allprojects { includeGroupByRegex 'com\\.github\\.chrisbanes' // PFLockScreen-Android includeGroupByRegex 'com\\.github\\.vector-im' + + //Chat effects + includeGroupByRegex 'com\\.github\\.jetradarmobile' + includeGroupByRegex 'nl\\.dionsegijn' } } maven { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt index 0f133323b0..a2b4e135d1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt @@ -33,4 +33,7 @@ object MessageType { // Add, in local, a fake message type in order to StickerMessage can inherit Message class // Because sticker isn't a message type but a event type without msgtype field const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker" + + const val MSGTYPE_CONFETTI = "nic.custom.confetti" + const val MSGTYPE_SNOW = "nic.custom.snow" } diff --git a/vector/build.gradle b/vector/build.gradle index 28d8fe5c1b..7bb8ca187c 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -410,6 +410,9 @@ dependencies { // Badge for compatibility implementation 'me.leolin:ShortcutBadger:1.1.22@aar' + // Chat effects + implementation 'nl.dionsegijn:konfetti:1.2.5' + implementation 'com.github.jetradarmobile:android-snowfall:1.2.0' // DI implementation "com.google.dagger:dagger:$daggerVersion" kapt "com.google.dagger:dagger-compiler:$daggerVersion" diff --git a/vector/src/main/assets/open_source_licenses.html b/vector/src/main/assets/open_source_licenses.html index acf0bec14f..bf341e38b7 100755 --- a/vector/src/main/assets/open_source_licenses.html +++ b/vector/src/main/assets/open_source_licenses.html @@ -390,6 +390,11 @@ SOFTWARE.
Copyright (C) 2016 - Niklas Baudy, Ruben Gees, Mario Đanić and contributors +
  • + JetradarMobile / android-snowfall +
    + Copyright 2016 JetRadar +
  •  Apache License
    @@ -576,5 +581,14 @@ Apache License
         
     
    +
    +    ISC License
    +    
  • + DanielMartinus / Konfetti +
    + Copyright (c) 2017 Dion Segijn +
  • +
    + diff --git a/vector/src/main/java/im/vector/app/features/command/Command.kt b/vector/src/main/java/im/vector/app/features/command/Command.kt index db429f9e58..9702f917c1 100644 --- a/vector/src/main/java/im/vector/app/features/command/Command.kt +++ b/vector/src/main/java/im/vector/app/features/command/Command.kt @@ -44,7 +44,8 @@ enum class Command(val command: String, val parameters: String, @StringRes val d POLL("/poll", "Question | Option 1 | Option 2 ...", R.string.command_description_poll), SHRUG("/shrug", "", R.string.command_description_shrug), PLAIN("/plain", "", R.string.command_description_plain), - DISCARD_SESSION("/discardsession", "", R.string.command_description_discard_session); + DISCARD_SESSION("/discardsession", "", R.string.command_description_discard_session), + CONFETTI("/confetti", "", R.string.command_confetti); val length get() = command.length + 1 diff --git a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt index 94de6bf265..0710faa47d 100644 --- a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt +++ b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt @@ -291,6 +291,10 @@ object CommandParser { Command.DISCARD_SESSION.command -> { ParsedCommand.DiscardSession } + Command.CONFETTI.command -> { + val message = textMessage.substring(Command.CONFETTI.command.length).trim() + ParsedCommand.Confetti(message) + } else -> { // Unknown command ParsedCommand.ErrorUnknownSlashCommand(slashCommand) diff --git a/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt b/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt index 2f8531929a..72c0ac496e 100644 --- a/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt +++ b/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt @@ -55,4 +55,5 @@ sealed class ParsedCommand { class SendShrug(val message: CharSequence) : ParsedCommand() class SendPoll(val question: String, val options: List) : ParsedCommand() object DiscardSession : ParsedCommand() + class Confetti(val message: String) : ParsedCommand() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/ChatEffectManager.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/ChatEffectManager.kt new file mode 100644 index 0000000000..71d751b494 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/ChatEffectManager.kt @@ -0,0 +1,125 @@ +/* + * 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.home.room.detail + +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.message.MessageContent +import org.matrix.android.sdk.api.session.room.model.message.MessageType +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import java.util.Timer +import java.util.TimerTask +import javax.inject.Inject + +enum class ChatEffect { + CONFETTI, + SNOW +} + +/** + * A simple chat effect manager helper class + * Used by the view model to know if an event that become visible should trigger a chat effect. + * It also manages effect duration and some cool down, for example if an effect is currently playing, + * any other trigger will be ignored + * For now it uses visibility callback to check for an effect (that means that a fail to decrypt event - more + * precisly an event decrypted with a few delay won't trigger an effect; it's acceptable) + * Events that are more that 10s old won't trigger effects + */ +class ChatEffectManager @Inject constructor() { + + interface Delegate { + fun stopEffects() + fun shouldStartEffect(effect: ChatEffect) + } + + var delegate: Delegate? = null + + private var stopTimer: Timer? = null + + // an in memory store to avoid trigger twice for an event (quick close/open timeline) + private val alreadyPlayed = emptyList().toMutableList() + + fun checkForEffect(event: TimelineEvent) { + val age = event.root.ageLocalTs ?: 0 + val now = System.currentTimeMillis() + // messages older than 10s should not trigger any effect + if ((now - age) >= 10_000) return + val content = event.root.getClearContent()?.toModel() ?: return + val effect = findEffect(content, event) + if (effect != null) { + synchronized(this) { + if (hasAlreadyPlayed(event)) return + markAsAlreadyPlayed(event) + // there is already an effect playing, so ignore + if (stopTimer != null) return + delegate?.shouldStartEffect(effect) + stopTimer = Timer().apply { + schedule(object : TimerTask() { + override fun run() { + stopEffect() + } + }, 6_000) + } + } + } + } + + fun dispose() { + stopTimer?.cancel() + stopTimer = null + delegate = null + alreadyPlayed.clear() + } + + @Synchronized + private fun stopEffect() { + stopTimer = null + delegate?.stopEffects() + } + + private fun markAsAlreadyPlayed(event: TimelineEvent) { + alreadyPlayed.add(event.eventId) + // also put the tx id as fast way to deal with local echo + event.root.unsignedData?.transactionId?.let { + alreadyPlayed.add(it) + } + } + + private fun hasAlreadyPlayed(event: TimelineEvent) : Boolean { + return alreadyPlayed.contains(event.eventId) + || (event.root.unsignedData?.transactionId?.let { alreadyPlayed.contains(it) } ?: false) + } + + private fun findEffect(content: MessageContent, event: TimelineEvent): ChatEffect? { + return when (content.msgType) { + MessageType.MSGTYPE_CONFETTI -> ChatEffect.CONFETTI + MessageType.MSGTYPE_SNOW -> ChatEffect.SNOW + + MessageType.MSGTYPE_TEXT -> { + val text = event.root.getClearContent().toModel()?.body ?: "" + if (text.contains("🎉") + || text.contains("🎊")) { + ChatEffect.CONFETTI + } else if (text.contains("⛄️") + || text.contains("☃️") + || text.contains("❄️")) { + ChatEffect.SNOW + } else null + } + else -> null + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index aff29bb7a3..4dd9c62a41 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -21,6 +21,7 @@ import android.app.Activity import android.content.DialogInterface import android.content.Intent import android.content.res.Configuration +import android.graphics.Color import android.graphics.Typeface import android.net.Uri import android.os.Build @@ -48,11 +49,14 @@ import androidx.core.text.toSpannable import androidx.core.util.Pair import androidx.core.view.ViewCompat import androidx.core.view.forEach +import androidx.core.view.isInvisible import androidx.core.view.isVisible import androidx.lifecycle.Observer import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import androidx.transition.TransitionManager +import butterknife.BindView import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.OnModelBuildFinishedListener import com.airbnb.epoxy.addGlidePreloader @@ -168,6 +172,8 @@ import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_room_detail.* import kotlinx.android.synthetic.main.composer_layout.view.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* +import nl.dionsegijn.konfetti.models.Shape +import nl.dionsegijn.konfetti.models.Size import org.billcarsonfr.jsonviewer.JSonViewerDialog import org.commonmark.parser.Parser import org.matrix.android.sdk.api.MatrixCallback @@ -378,6 +384,8 @@ class RoomDetailFragment @Inject constructor( is RoomDetailViewEvents.ShowRoomAvatarFullScreen -> it.matrixItem?.let { item -> navigator.openBigImageViewer(requireActivity(), it.view, item) } + is RoomDetailViewEvents.StartChatEffect -> handleChatEffect(it.type) + RoomDetailViewEvents.StopChatEffects -> handleStopChatEffects() }.exhaustive } @@ -386,6 +394,34 @@ class RoomDetailFragment @Inject constructor( } } + private fun handleChatEffect(chatEffect: ChatEffect) { + when (chatEffect) { + ChatEffect.CONFETTI -> { + viewKonfetti.isVisible = true + viewKonfetti.build() + .addColors(Color.YELLOW, Color.GREEN, Color.MAGENTA) + .setDirection(0.0, 359.0) + .setSpeed(2f, 5f) + .setFadeOutEnabled(true) + .setTimeToLive(2000L) + .addShapes(Shape.Square, Shape.Circle) + .addSizes(Size(12)) + .setPosition(-50f, viewKonfetti.width + 50f, -50f, -50f) + .streamFor(150, 3000L) + } + ChatEffect.SNOW -> { + viewSnowFall.isVisible = true + viewSnowFall.restartFalling() + } + } + } + private fun handleStopChatEffects() { + TransitionManager.beginDelayedTransition(rootConstraintLayout) + viewSnowFall.isVisible = false + // when gone the effect is a bit buggy + viewKonfetti.isInvisible = true + } + override fun onImageReady(uri: Uri?) { uri ?: return roomDetailViewModel.handle( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt index d5d94a0ca5..81d3d622e7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt @@ -95,4 +95,7 @@ sealed class RoomDetailViewEvents : VectorViewEvents { // TODO Remove object SlashCommandNotImplemented : SendMessageResult() + + data class StartChatEffect(val type: ChatEffect) : RoomDetailViewEvents() + object StopChatEffects : RoomDetailViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 21858438b9..75423dabfb 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -98,7 +98,6 @@ import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap import timber.log.Timber import java.io.File -import java.lang.Exception import java.util.UUID import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -115,8 +114,9 @@ class RoomDetailViewModel @AssistedInject constructor( private val roomSummaryHolder: RoomSummaryHolder, private val typingHelper: TypingHelper, private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager, + private val chatEffectManager: ChatEffectManager, timelineSettingsFactory: TimelineSettingsFactory -) : VectorViewModel(initialState), Timeline.Listener { +) : VectorViewModel(initialState), Timeline.Listener, ChatEffectManager.Delegate { private val room = session.getRoom(initialState.roomId)!! private val eventId = initialState.eventId @@ -171,6 +171,7 @@ class RoomDetailViewModel @AssistedInject constructor( room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear() // Inform the SDK that the room is displayed session.onRoomDisplayed(initialState.roomId) + chatEffectManager.delegate = this } private fun observePowerLevel() { @@ -225,58 +226,58 @@ class RoomDetailViewModel @AssistedInject constructor( override fun handle(action: RoomDetailAction) { when (action) { - is RoomDetailAction.UserIsTyping -> handleUserIsTyping(action) - is RoomDetailAction.SaveDraft -> handleSaveDraft(action) - is RoomDetailAction.SendMessage -> handleSendMessage(action) - is RoomDetailAction.SendMedia -> handleSendMedia(action) - is RoomDetailAction.SendSticker -> handleSendSticker(action) - is RoomDetailAction.TimelineEventTurnsVisible -> handleEventVisible(action) - is RoomDetailAction.TimelineEventTurnsInvisible -> handleEventInvisible(action) - is RoomDetailAction.LoadMoreTimelineEvents -> handleLoadMore(action) - is RoomDetailAction.SendReaction -> handleSendReaction(action) - is RoomDetailAction.AcceptInvite -> handleAcceptInvite() - is RoomDetailAction.RejectInvite -> handleRejectInvite() - is RoomDetailAction.RedactAction -> handleRedactEvent(action) - is RoomDetailAction.UndoReaction -> handleUndoReact(action) - is RoomDetailAction.UpdateQuickReactAction -> handleUpdateQuickReaction(action) - is RoomDetailAction.EnterRegularMode -> handleEnterRegularMode(action) - is RoomDetailAction.EnterEditMode -> handleEditAction(action) - is RoomDetailAction.EnterQuoteMode -> handleQuoteAction(action) - is RoomDetailAction.EnterReplyMode -> handleReplyAction(action) - is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action) - is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action) - is RoomDetailAction.HandleTombstoneEvent -> handleTombstoneEvent(action) - is RoomDetailAction.ResendMessage -> handleResendEvent(action) - is RoomDetailAction.RemoveFailedEcho -> handleRemove(action) - is RoomDetailAction.ResendAll -> handleResendAll() - is RoomDetailAction.MarkAllAsRead -> handleMarkAllAsRead() - is RoomDetailAction.ReportContent -> handleReportContent(action) - is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action) + is RoomDetailAction.UserIsTyping -> handleUserIsTyping(action) + is RoomDetailAction.SaveDraft -> handleSaveDraft(action) + is RoomDetailAction.SendMessage -> handleSendMessage(action) + is RoomDetailAction.SendMedia -> handleSendMedia(action) + is RoomDetailAction.SendSticker -> handleSendSticker(action) + is RoomDetailAction.TimelineEventTurnsVisible -> handleEventVisible(action) + is RoomDetailAction.TimelineEventTurnsInvisible -> handleEventInvisible(action) + is RoomDetailAction.LoadMoreTimelineEvents -> handleLoadMore(action) + is RoomDetailAction.SendReaction -> handleSendReaction(action) + is RoomDetailAction.AcceptInvite -> handleAcceptInvite() + is RoomDetailAction.RejectInvite -> handleRejectInvite() + is RoomDetailAction.RedactAction -> handleRedactEvent(action) + is RoomDetailAction.UndoReaction -> handleUndoReact(action) + is RoomDetailAction.UpdateQuickReactAction -> handleUpdateQuickReaction(action) + is RoomDetailAction.EnterRegularMode -> handleEnterRegularMode(action) + is RoomDetailAction.EnterEditMode -> handleEditAction(action) + is RoomDetailAction.EnterQuoteMode -> handleQuoteAction(action) + is RoomDetailAction.EnterReplyMode -> handleReplyAction(action) + is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action) + is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action) + is RoomDetailAction.HandleTombstoneEvent -> handleTombstoneEvent(action) + is RoomDetailAction.ResendMessage -> handleResendEvent(action) + is RoomDetailAction.RemoveFailedEcho -> handleRemove(action) + is RoomDetailAction.ResendAll -> handleResendAll() + is RoomDetailAction.MarkAllAsRead -> handleMarkAllAsRead() + is RoomDetailAction.ReportContent -> handleReportContent(action) + is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action) is RoomDetailAction.EnterTrackingUnreadMessagesState -> startTrackingUnreadMessages() - is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages() - is RoomDetailAction.ReplyToOptions -> handleReplyToOptions(action) - is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action) - is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action) - is RoomDetailAction.RequestVerification -> handleRequestVerification(action) - is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action) - is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action) - is RoomDetailAction.TapOnFailedToDecrypt -> handleTapOnFailedToDecrypt(action) - is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment() - is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager() - is RoomDetailAction.StartCall -> handleStartCall(action) - is RoomDetailAction.EndCall -> handleEndCall() - is RoomDetailAction.ManageIntegrations -> handleManageIntegrations() - is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action) - is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId) - is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action) - is RoomDetailAction.CancelSend -> handleCancel(action) - is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action) - is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action) - RoomDetailAction.QuickActionInvitePeople -> handleInvitePeople() - RoomDetailAction.QuickActionSetAvatar -> handleQuickSetAvatar() - is RoomDetailAction.SetAvatarAction -> handleSetNewAvatar(action) - RoomDetailAction.QuickActionSetTopic -> _viewEvents.post(RoomDetailViewEvents.OpenRoomSettings) - is RoomDetailAction.ShowRoomAvatarFullScreen -> { + is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages() + is RoomDetailAction.ReplyToOptions -> handleReplyToOptions(action) + is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action) + is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action) + is RoomDetailAction.RequestVerification -> handleRequestVerification(action) + is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action) + is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action) + is RoomDetailAction.TapOnFailedToDecrypt -> handleTapOnFailedToDecrypt(action) + is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment() + is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager() + is RoomDetailAction.StartCall -> handleStartCall(action) + is RoomDetailAction.EndCall -> handleEndCall() + is RoomDetailAction.ManageIntegrations -> handleManageIntegrations() + is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action) + is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId) + is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action) + is RoomDetailAction.CancelSend -> handleCancel(action) + is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action) + is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action) + RoomDetailAction.QuickActionInvitePeople -> handleInvitePeople() + RoomDetailAction.QuickActionSetAvatar -> handleQuickSetAvatar() + is RoomDetailAction.SetAvatarAction -> handleSetNewAvatar(action) + RoomDetailAction.QuickActionSetTopic -> _viewEvents.post(RoomDetailViewEvents.OpenRoomSettings) + is RoomDetailAction.ShowRoomAvatarFullScreen -> { _viewEvents.post( RoomDetailViewEvents.ShowRoomAvatarFullScreen(action.matrixItem, action.transitionView) ) @@ -549,7 +550,7 @@ class RoomDetailViewModel @AssistedInject constructor( SendMode.EDIT(timelineEvent, currentDraft.text) } } - else -> null + else -> null } ?: SendMode.REGULAR("", fromSharing = false) ) } @@ -592,16 +593,16 @@ class RoomDetailViewModel @AssistedInject constructor( return@withState false } when (itemId) { - R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true - R.id.timeline_setting -> true - R.id.invite -> state.canInvite - R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true - R.id.open_matrix_apps -> true + R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true + R.id.timeline_setting -> true + R.id.invite -> state.canInvite + R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true + R.id.open_matrix_apps -> true R.id.voice_call, - R.id.video_call -> true // always show for discoverability - R.id.hangup_call -> webRtcPeerConnectionManager.currentCall != null - R.id.search -> true - else -> false + R.id.video_call -> true // always show for discoverability + R.id.hangup_call -> webRtcPeerConnectionManager.currentCall != null + R.id.search -> true + else -> false } } @@ -714,6 +715,11 @@ class RoomDetailViewModel @AssistedInject constructor( _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) popDraft() } + is ParsedCommand.Confetti -> { + room.sendTextMessage(slashCommandResult.message, MessageType.MSGTYPE_CONFETTI) + _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) + popDraft() + } is ParsedCommand.SendPoll -> { room.sendPoll(slashCommandResult.question, slashCommandResult.options.mapIndexed { index, s -> OptionItem(s, "$index. $s") }) _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) @@ -742,7 +748,7 @@ class RoomDetailViewModel @AssistedInject constructor( } }.exhaustive } - is SendMode.EDIT -> { + is SendMode.EDIT -> { // is original event a reply? val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel()?.relatesTo?.inReplyTo?.eventId ?: state.sendMode.timelineEvent.root.content.toModel()?.relatesTo?.inReplyTo?.eventId @@ -768,7 +774,7 @@ class RoomDetailViewModel @AssistedInject constructor( _viewEvents.post(RoomDetailViewEvents.MessageSent) popDraft() } - is SendMode.QUOTE -> { + is SendMode.QUOTE -> { val messageContent: MessageContent? = state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel() ?: state.sendMode.timelineEvent.root.getClearContent().toModel() @@ -791,7 +797,7 @@ class RoomDetailViewModel @AssistedInject constructor( _viewEvents.post(RoomDetailViewEvents.MessageSent) popDraft() } - is SendMode.REPLY -> { + is SendMode.REPLY -> { state.sendMode.timelineEvent.let { room.replyToMessage(it, action.text.toString(), action.autoMarkdown) _viewEvents.post(RoomDetailViewEvents.MessageSent) @@ -983,9 +989,29 @@ class RoomDetailViewModel @AssistedInject constructor( visibleEventsObservable.accept(RoomDetailAction.TimelineEventTurnsVisible(event)) } } + + // handle chat effects here + if (vectorPreferences.chatEffectsEnabled()) { + chatEffectManager.checkForEffect(action.event) + } } } + override fun shouldStartEffect(effect: ChatEffect) { + when (effect) { + ChatEffect.CONFETTI -> { + _viewEvents.post(RoomDetailViewEvents.StartChatEffect(ChatEffect.CONFETTI)) + } + ChatEffect.SNOW -> { + _viewEvents.post(RoomDetailViewEvents.StartChatEffect(ChatEffect.SNOW)) + } + } + } + + override fun stopEffects() { + _viewEvents.post(RoomDetailViewEvents.StopChatEffects) + } + private fun handleLoadMore(action: RoomDetailAction.LoadMoreTimelineEvents) { timeline.paginate(action.direction, PAGINATION_COUNT) } @@ -1387,6 +1413,7 @@ class RoomDetailViewModel @AssistedInject constructor( if (vectorPreferences.sendTypingNotifs()) { room.userStopsTyping() } + chatEffectManager.dispose() super.onCleared() } } 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 c50692df82..16be2b1552 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 @@ -97,6 +97,7 @@ class VectorPreferences @Inject constructor(private val context: Context) { private const val SETTINGS_SHOW_AVATAR_DISPLAY_NAME_CHANGES_MESSAGES_KEY = "SETTINGS_SHOW_AVATAR_DISPLAY_NAME_CHANGES_MESSAGES_KEY" private const val SETTINGS_VIBRATE_ON_MENTION_KEY = "SETTINGS_VIBRATE_ON_MENTION_KEY" private const val SETTINGS_SEND_MESSAGE_WITH_ENTER = "SETTINGS_SEND_MESSAGE_WITH_ENTER" + private const val SETTINGS_ENABLE_CHAT_EFFECTS = "SETTINGS_ENABLE_CHAT_EFFECTS" // Help private const val SETTINGS_SHOULD_SHOW_HELP_ON_ROOM_LIST_KEY = "SETTINGS_SHOULD_SHOW_HELP_ON_ROOM_LIST_KEY" @@ -869,6 +870,10 @@ class VectorPreferences @Inject constructor(private val context: Context) { return defaultPrefs.getBoolean(SETTINGS_SECURITY_USE_GRACE_PERIOD_FLAG, true) } + fun chatEffectsEnabled(): Boolean { + return defaultPrefs.getBoolean(SETTINGS_ENABLE_CHAT_EFFECTS, true) + } + /** * Return true if Pin code is disabled, or if user set the settings to see full notification content */ diff --git a/vector/src/main/res/drawable-anydpi-v26/snow.png b/vector/src/main/res/drawable-anydpi-v26/snow.png new file mode 100644 index 0000000000000000000000000000000000000000..0142772cedb2cfe82fe2855d7ef9b0bc6febbafc GIT binary patch literal 1986 zcmV;z2R-Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91HlPCl1ONa40RR91HUIzs0P{mGqyPX1EJ;K`RA>d&n$2reXB5V3&3BU; zO>0mrLB&!^OQAHx3Q=?sn*IY_xKIlH3$h8tg&?$U6kU{B-MGj?sGDr6NF#P3wxrcU zg{~B#mT1zLhI}WJq|fj6ea3gjj58C*WahxhdB4x|+;i?b_r5n9UMa0?2+P@0jmxpO z<*4GaZPc~8VlJn}ti~E%$K}|vmixV~31G98F>dqtqHVUrLKpD7n82%=OdT6*JcjiR z8oQLGZi~lw4z^&aDtP46ficjSz(x&WJ#4Hd%sj-PA+?yLsZfszGaj-1zn15(o#h#p zbjO4!8$4vtYP7DduKMD|iyyVOw;yV1YI>`-wsvbxO%3{{`j_YD=l_uo&&|yZPfSew zH8?nU{piu7zs}CiE~wuML6d~MFNUp}N+S^A%0s3`S}$#qwn(>0+pb)>a^&H|hu<$O zEQ~A{Da>ImYp_;WleO`IFMQ(L(O2=s#iWQzt@JhNW@)Rmt*@`|v+?oq--_gtb2w|U zCTj;D_`)Z?!B7%%PC3%7PRbR0L%QR{i4%uLMn-G;4GK7(&CnB1x&XHtZo9A`mNZ|9vmcfJy$R9;l2 z8<4^$z5@oZfT`%Tj7f2IOlsGI2M_v6hqt(u@GUI6q+DP}t|)SZm>lM2C9g@powsk_ z{<=6Qr5_CjumntC1EY6K6>|&GVj$ZqI}uxM+vUrbzYxr|Out-LB?T5R1#DpSPXX9- z3eoa?#WuU+*s)_>Yq3$}Vv^ks7BGQL3dZ<;Q}tPKw48$E@}5VJ9$n8~e7);n0-F?! zaZ0A6MuaRh`u-xkbr&vN=xA+iJ?H`DV!;G9FiM?@U{4b>RvNrvu;cKU^v>AW*zfBd zom~FG2F8FD%-%>6a*h}klsflV&81714m3A6f1q+%3fRCXa6~NL_A`h9-f75_Mr}t& zM>mb-EW-GS@DV96XKeeWKdBw!^CeUGIh1_3IL z*v9($dS2rzLIPGuJwq&EsE#x_4d!LCA|znVASK`#sR*$|bs)pTZ$(JJn#L~Bl#}!` zfw*(ojTKl&d%MrTucNRh84YDz$%Qp02J=Lxr>95Dm`etnSO#1k zNqCUtZ8UwDqw>efEp+bf1~?zQsuG`uz*Qm zrlnv6E11(8hsqfeM~y>B@^+83{p{JZy-Q0=i`iFht?OU_3zz~nFs^uDs`d=wp5twl zW8>tATlQV)zN=TSe!JF0vi*SpECEx&N8B2p{9tph`Nul>D53iM(oTJ+>d$tzR&{)X zAz*2f1zW)%B{VjDM8HNun3K;)vK*f7*9Y)(Ynhh#!Y951hMlrtic>J&-PN_SaIZNf zIU8q}B$szdA=i2O^yx44FAWnfJ6ia_7e0e;Fn|S2VB@v{E0_zCm>l<;$#Jzv%YBCr zA3k#b{{34oiyUj?17E=>zD+9rbW|`Y%}>PSs#SC3NCEPj4TYWQpuSc03=IwaSt^38 z#hR=geBcY8_;#eg^xR10V{39O!luH4+9t}@V`JmyhK7dZ6-cl0W0RAUV|p!mGCDdsq>bU1lP6E!)tAcT zF;4410)-H5sDFfcCSfXUdJ6c@pBS;xQtnoZ6mA&h#A=h&+2`BDrquVj!| z?merc=!n|-CbutxbJPspB(d~aJsw-I*jd37!a&c%T1~)2ZQbuVF;IiIrH^tv2A4&V zVuGDfUac57^IeXu+vD{OG$!COk6TswKR+Ea U8gR6@i~s-t07*qoM6N<$f@hzt8UO$Q literal 0 HcmV?d00001 diff --git a/vector/src/main/res/drawable/ic_snow.xml b/vector/src/main/res/drawable/ic_snow.xml new file mode 100644 index 0000000000..4149adab9f --- /dev/null +++ b/vector/src/main/res/drawable/ic_snow.xml @@ -0,0 +1,13 @@ + + + diff --git a/vector/src/main/res/layout/fragment_room_detail.xml b/vector/src/main/res/layout/fragment_room_detail.xml index 33f462c0d1..d33f47872d 100644 --- a/vector/src/main/res/layout/fragment_room_detail.xml +++ b/vector/src/main/res/layout/fragment_room_detail.xml @@ -6,6 +6,21 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 46873accfc..f474af84c8 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -877,6 +877,8 @@ Show read receipts Click on the read receipts for a detailed list. Show room member state events + Show chat effects + Use /confetti command or send a message containing ❄️ or 🎉 Includes invite/join/left/kick/ban events and avatar/display name changes. Show join and leave events Invites, kicks, and bans are unaffected. @@ -2568,6 +2570,8 @@ Show %d devices you can verify with now + Sends the given message with confetti + Unencrypted Encrypted by an unverified device Review where you’re logged in diff --git a/vector/src/main/res/values/theme_dark.xml b/vector/src/main/res/values/theme_dark.xml index 6be0adb907..86fbb57608 100644 --- a/vector/src/main/res/values/theme_dark.xml +++ b/vector/src/main/res/values/theme_dark.xml @@ -200,6 +200,9 @@ @style/WidgetButtonSocialLogin.Facebook.Dark @style/WidgetButtonSocialLogin.Twitter.Dark @style/WidgetButtonSocialLogin.Apple.Dark + + + @android:color/transparent