From 3d5d9ad154dd9f5437ca8c7177e561e390008831 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 Nov 2021 15:46:27 +0100 Subject: [PATCH 1/3] Iterate on the consent dialog of the identity server. --- changelog.d/4577.feature | 1 + .../java/im/vector/app/core/utils/Dialogs.kt | 42 +++++++++++++++---- .../contactsbook/ContactsBookFragment.kt | 5 +-- .../discovery/DiscoverySettingsFragment.kt | 2 +- .../userdirectory/UserListFragment.kt | 5 +-- vector/src/main/res/values/strings.xml | 5 +++ 6 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 changelog.d/4577.feature diff --git a/changelog.d/4577.feature b/changelog.d/4577.feature new file mode 100644 index 0000000000..56ce827769 --- /dev/null +++ b/changelog.d/4577.feature @@ -0,0 +1 @@ +Iterate on the consent dialog of the identity server. \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt index ad62b0fcb1..6847c1661c 100644 --- a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt +++ b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt @@ -17,10 +17,15 @@ package im.vector.app.core.utils import android.content.Context +import android.text.method.LinkMovementMethod import android.webkit.WebView import android.webkit.WebViewClient +import android.widget.TextView import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R +import im.vector.app.features.discovery.IdentityServerPolicy +import me.gujun.android.span.link +import me.gujun.android.span.span /** * Open a web view above the current activity. @@ -40,16 +45,37 @@ fun Context.displayInWebView(url: String) { .show() } -fun Context.showIdentityServerConsentDialog(configuredIdentityServer: String?, policyLinkCallback: () -> Unit, consentCallBack: (() -> Unit)) { +fun Context.showIdentityServerConsentDialog(configuredIdentityServer: String?, + policies: List?, + consentCallBack: (() -> Unit)) { + // Build the message + val content = span { + +getString(R.string.identity_server_consent_dialog_content_3) + +"\n\n" + if (!policies.isNullOrEmpty()) { + span { + textStyle = "bold" + text = getString(R.string.settings_privacy_policy) + } + policies.forEach { + +"\n • " + // Use the url as the text too + link(it.url, it.url) + } + +"\n\n" + } + +getString(R.string.identity_server_consent_dialog_content_question) + } MaterialAlertDialogBuilder(this) - .setTitle(getString(R.string.identity_server_consent_dialog_title_2, configuredIdentityServer ?: "")) - .setMessage(R.string.identity_server_consent_dialog_content_2) - .setPositiveButton(R.string.yes) { _, _ -> + .setTitle(getString(R.string.identity_server_consent_dialog_title_2, configuredIdentityServer.orEmpty())) + .setMessage(content) + .setPositiveButton(R.string.reactions_agree) { _, _ -> consentCallBack.invoke() } - .setNeutralButton(R.string.identity_server_consent_dialog_neutral_policy) { _, _ -> - policyLinkCallback.invoke() - } - .setNegativeButton(R.string.no, null) + .setNegativeButton(R.string.action_not_now, null) .show() + .apply { + // Make the link(s) clickable. Must be called after show() + (findViewById(android.R.id.message) as? TextView)?.movementMethod = LinkMovementMethod.getInstance() + } } diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt index 95c6105912..c98763714b 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt @@ -30,7 +30,6 @@ import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.showIdentityServerConsentDialog import im.vector.app.databinding.FragmentContactsBookBinding -import im.vector.app.features.navigation.SettingsActivityPayload import im.vector.app.features.userdirectory.PendingSelection import im.vector.app.features.userdirectory.UserListAction import im.vector.app.features.userdirectory.UserListSharedAction @@ -75,9 +74,7 @@ class ContactsBookFragment @Inject constructor( withState(contactsBookViewModel) { state -> requireContext().showIdentityServerConsentDialog( state.identityServerUrl, - policyLinkCallback = { - navigator.openSettings(requireContext(), SettingsActivityPayload.DiscoverySettings(expandIdentityPolicies = true)) - }, + /* TODO */ emptyList(), consentCallBack = { contactsBookViewModel.handle(ContactsBookAction.UserConsentGranted) } ) } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index 7306146027..0b0236e000 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -187,7 +187,7 @@ class DiscoverySettingsFragment @Inject constructor( withState(viewModel) { state -> requireContext().showIdentityServerConsentDialog( state.identityServer.invoke()?.serverUrl, - policyLinkCallback = { viewModel.handle(DiscoverySettingsAction.SetPoliciesExpandState(expanded = true)) }, + state.identityServer.invoke()?.policies, consentCallBack = { viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(true)) } ) } diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt index 2b4d4f6f1e..5ac1a83a6d 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt @@ -42,7 +42,6 @@ import im.vector.app.core.utils.showIdentityServerConsentDialog import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.databinding.FragmentUserListBinding import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel -import im.vector.app.features.navigation.SettingsActivityPayload import im.vector.app.features.settings.VectorSettingsActivity import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -234,9 +233,7 @@ class UserListFragment @Inject constructor( withState(viewModel) { state -> requireContext().showIdentityServerConsentDialog( state.configuredIdentityServer, - policyLinkCallback = { - navigator.openSettings(requireContext(), SettingsActivityPayload.DiscoverySettings(expandIdentityPolicies = true)) - }, + /* TODO */ emptyList(), consentCallBack = { viewModel.handle(UserListAction.UpdateUserConsent(true)) } ) } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index dec9e6e8db..ddb731a5e0 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -456,6 +456,7 @@ Copied to clipboard Disable Return + Not now Confirmation @@ -2193,7 +2194,9 @@ Your rooms will be displayed here. Tap the + bottom right to find existing ones or start some of your own. Reactions + Agree + Like Add Reaction View Reactions @@ -2377,6 +2380,8 @@ Send emails and phone numbers to %s In order to discover existing contacts you know, do you accept to send your contact data (phone numbers and/or emails) to the configured identity server (%1$s)?\n\nFor more privacy, the sent data will be hashed before being sent. To discover existing contacts, you need to send contact info to your identity server.\n\nWe hash your data before sending for privacy. Do you consent to send this info? + To discover existing contacts, you need to send contact info (emails and phone numbers) to your identity server. We hash your data before sending for privacy. + Do you agree to send this info? Policy Enter an identity server URL From b66aff457a445440c44acc4b432d6a67eba534a0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 Nov 2021 16:34:16 +0100 Subject: [PATCH 2/3] Iterate on the consent dialog of the identity server - handle the other places. --- .../java/im/vector/app/core/utils/Dialogs.kt | 11 +++-- .../contactsbook/ContactsBookAction.kt | 1 + .../contactsbook/ContactsBookFragment.kt | 22 ++++++---- .../contactsbook/ContactsBookViewEvents.kt | 25 ++++++++++++ .../contactsbook/ContactsBookViewModel.kt | 29 ++++++++++---- .../discovery/DiscoverySettingsFragment.kt | 3 +- .../discovery/DiscoverySettingsViewModel.kt | 22 +--------- .../app/features/discovery/Extensions.kt | 40 +++++++++++++++++++ .../features/userdirectory/UserListAction.kt | 1 + .../userdirectory/UserListFragment.kt | 17 ++++---- .../userdirectory/UserListViewEvents.kt | 3 ++ .../userdirectory/UserListViewModel.kt | 25 ++++++++++-- 12 files changed, 147 insertions(+), 52 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewEvents.kt create mode 100644 vector/src/main/java/im/vector/app/features/discovery/Extensions.kt diff --git a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt index 6847c1661c..ad01546782 100644 --- a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt +++ b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt @@ -23,7 +23,7 @@ import android.webkit.WebViewClient import android.widget.TextView import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R -import im.vector.app.features.discovery.IdentityServerPolicy +import im.vector.app.features.discovery.IdentityServerWithTerms import me.gujun.android.span.link import me.gujun.android.span.span @@ -45,19 +45,18 @@ fun Context.displayInWebView(url: String) { .show() } -fun Context.showIdentityServerConsentDialog(configuredIdentityServer: String?, - policies: List?, +fun Context.showIdentityServerConsentDialog(identityServerWithTerms: IdentityServerWithTerms?, consentCallBack: (() -> Unit)) { // Build the message val content = span { +getString(R.string.identity_server_consent_dialog_content_3) +"\n\n" - if (!policies.isNullOrEmpty()) { + if (identityServerWithTerms?.policies?.isNullOrEmpty() == false) { span { textStyle = "bold" text = getString(R.string.settings_privacy_policy) } - policies.forEach { + identityServerWithTerms.policies.forEach { +"\n • " // Use the url as the text too link(it.url, it.url) @@ -67,7 +66,7 @@ fun Context.showIdentityServerConsentDialog(configuredIdentityServer: String?, +getString(R.string.identity_server_consent_dialog_content_question) } MaterialAlertDialogBuilder(this) - .setTitle(getString(R.string.identity_server_consent_dialog_title_2, configuredIdentityServer.orEmpty())) + .setTitle(getString(R.string.identity_server_consent_dialog_title_2, identityServerWithTerms?.serverUrl.orEmpty())) .setMessage(content) .setPositiveButton(R.string.reactions_agree) { _, _ -> consentCallBack.invoke() diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookAction.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookAction.kt index e380998fd2..6ab8174e0e 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookAction.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookAction.kt @@ -21,5 +21,6 @@ import im.vector.app.core.platform.VectorViewModelAction sealed class ContactsBookAction : VectorViewModelAction { data class FilterWith(val filter: String) : ContactsBookAction() data class OnlyBoundContacts(val onlyBoundContacts: Boolean) : ContactsBookAction() + object UserConsentRequest : ContactsBookAction() object UserConsentGranted : ContactsBookAction() } diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt index c98763714b..c1bccf855d 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt @@ -26,6 +26,7 @@ import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState 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.extensions.hideKeyboard import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.showIdentityServerConsentDialog @@ -67,20 +68,27 @@ class ContactsBookFragment @Inject constructor( setupConsentView() setupOnlyBoundContactsView() setupCloseView() + contactsBookViewModel.observeViewEvents { + when (it) { + is ContactsBookViewEvents.Failure -> showFailure(it.throwable) + is ContactsBookViewEvents.OnPoliciesRetrieved -> showConsentDialog(it) + }.exhaustive + } } private fun setupConsentView() { views.phoneBookSearchForMatrixContacts.setOnClickListener { - withState(contactsBookViewModel) { state -> - requireContext().showIdentityServerConsentDialog( - state.identityServerUrl, - /* TODO */ emptyList(), - consentCallBack = { contactsBookViewModel.handle(ContactsBookAction.UserConsentGranted) } - ) - } + contactsBookViewModel.handle(ContactsBookAction.UserConsentRequest) } } + private fun showConsentDialog(event: ContactsBookViewEvents.OnPoliciesRetrieved) { + requireContext().showIdentityServerConsentDialog( + event.identityServerWithTerms, + consentCallBack = { contactsBookViewModel.handle(ContactsBookAction.UserConsentGranted) } + ) + } + private fun setupOnlyBoundContactsView() { views.phoneBookOnlyBoundContacts.checkedChanges() .onEach { diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewEvents.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewEvents.kt new file mode 100644 index 0000000000..c7fd13a62c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewEvents.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 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.contactsbook + +import im.vector.app.core.platform.VectorViewEvents +import im.vector.app.features.discovery.IdentityServerWithTerms + +sealed class ContactsBookViewEvents : VectorViewEvents { + data class Failure(val throwable: Throwable) : ContactsBookViewEvents() + data class OnPoliciesRetrieved(val identityServerWithTerms: IdentityServerWithTerms?) : ContactsBookViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt index 7f0dbcb7b2..5678668b25 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt @@ -16,20 +16,21 @@ package im.vector.app.features.contactsbook -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.R import im.vector.app.core.contacts.ContactsDataSource import im.vector.app.core.contacts.MappedContact import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive -import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.discovery.fetchIdentityServerWithTerms import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session @@ -37,11 +38,12 @@ import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.ThreePid import timber.log.Timber -class ContactsBookViewModel @AssistedInject constructor(@Assisted - initialState: ContactsBookViewState, - private val contactsDataSource: ContactsDataSource, - private val session: Session) : - VectorViewModel(initialState) { +class ContactsBookViewModel @AssistedInject constructor( + @Assisted initialState: ContactsBookViewState, + private val contactsDataSource: ContactsDataSource, + private val stringProvider: StringProvider, + private val session: Session +) : VectorViewModel(initialState) { @AssistedFactory interface Factory : MavericksAssistedViewModelFactory { @@ -162,9 +164,22 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted is ContactsBookAction.FilterWith -> handleFilterWith(action) is ContactsBookAction.OnlyBoundContacts -> handleOnlyBoundContacts(action) ContactsBookAction.UserConsentGranted -> handleUserConsentGranted() + ContactsBookAction.UserConsentRequest -> handleUserConsentRequest() }.exhaustive } + private fun handleUserConsentRequest() { + viewModelScope.launch { + val event = try { + val result = session.fetchIdentityServerWithTerms(stringProvider.getString(R.string.resources_language)) + ContactsBookViewEvents.OnPoliciesRetrieved(result) + } catch (throwable: Throwable) { + ContactsBookViewEvents.Failure(throwable) + } + _viewEvents.post(event) + } + } + private fun handleUserConsentGranted() { session.identityService().setUserConsent(true) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index 0b0236e000..9c46fc2089 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -186,8 +186,7 @@ class DiscoverySettingsFragment @Inject constructor( if (newValue) { withState(viewModel) { state -> requireContext().showIdentityServerConsentDialog( - state.identityServer.invoke()?.serverUrl, - state.identityServer.invoke()?.policies, + state.identityServer.invoke(), consentCallBack = { viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(true)) } ) } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index b02784dad9..4eb3fada28 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -30,7 +30,6 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.core.utils.ensureProtocol import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -39,7 +38,6 @@ import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid -import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.flow.flow class DiscoverySettingsViewModel @AssistedInject constructor( @@ -56,7 +54,6 @@ class DiscoverySettingsViewModel @AssistedInject constructor( companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val identityService = session.identityService() - private val termsService: TermsService = session private val identityServerManagerListener = object : IdentityServiceListener { override fun onIdentityServerChange() = withState { state -> @@ -397,7 +394,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } } viewModelScope.launch { - runCatching { fetchIdentityServerWithTerms() }.fold( + runCatching { session.fetchIdentityServerWithTerms(stringProvider.getString(R.string.resources_language)) }.fold( onSuccess = { setState { copy(identityServer = Success(it)) } }, onFailure = { _viewEvents.post(DiscoverySettingsViewEvents.Failure(it)) } ) @@ -405,21 +402,6 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } private suspend fun fetchIdentityServerWithTerms(): IdentityServerWithTerms? { - val identityServerUrl = identityService.getCurrentIdentityServerUrl() - return identityServerUrl?.let { - val terms = termsService.getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol()) - .serverResponse - .getLocalizedTerms(stringProvider.getString(R.string.resources_language)) - val policyUrls = terms.mapNotNull { - val name = it.localizedName ?: it.policyName - val url = it.localizedUrl - if (name == null || url == null) { - null - } else { - IdentityServerPolicy(name = name, url = url) - } - } - IdentityServerWithTerms(identityServerUrl, policyUrls) - } + return session.fetchIdentityServerWithTerms(stringProvider.getString(R.string.resources_language)) } } diff --git a/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt b/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt new file mode 100644 index 0000000000..bf6bd89938 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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.discovery + +import im.vector.app.core.utils.ensureProtocol +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.terms.TermsService + +suspend fun Session.fetchIdentityServerWithTerms(userLanguage: String): IdentityServerWithTerms? { + val identityServerUrl = identityService().getCurrentIdentityServerUrl() + return identityServerUrl?.let { + val terms = getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol()) + .serverResponse + .getLocalizedTerms(userLanguage) + val policyUrls = terms.mapNotNull { + val name = it.localizedName ?: it.policyName + val url = it.localizedUrl + if (name == null || url == null) { + null + } else { + IdentityServerPolicy(name = name, url = url) + } + } + IdentityServerWithTerms(identityServerUrl, policyUrls) + } +} diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListAction.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListAction.kt index 86de26ac23..d5b049e328 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListAction.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListAction.kt @@ -24,6 +24,7 @@ sealed class UserListAction : VectorViewModelAction { data class AddPendingSelection(val pendingSelection: PendingSelection) : UserListAction() data class RemovePendingSelection(val pendingSelection: PendingSelection) : UserListAction() object ComputeMatrixToLinkForSharing : UserListAction() + object UserConsentRequest : UserListAction() data class UpdateUserConsent(val consent: Boolean) : UserListAction() object Resumed : UserListAction() } diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt index 5ac1a83a6d..721bce4af9 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt @@ -102,6 +102,8 @@ class UserListFragment @Inject constructor( extraTitle = getString(R.string.invite_friends_rich_title) ) } + is UserListViewEvents.Failure -> showFailure(it.throwable) + is UserListViewEvents.OnPoliciesRetrieved -> showConsentDialog(it) } } } @@ -230,13 +232,14 @@ class UserListFragment @Inject constructor( } override fun giveIdentityServerConsent() { - withState(viewModel) { state -> - requireContext().showIdentityServerConsentDialog( - state.configuredIdentityServer, - /* TODO */ emptyList(), - consentCallBack = { viewModel.handle(UserListAction.UpdateUserConsent(true)) } - ) - } + viewModel.handle(UserListAction.UserConsentRequest) + } + + private fun showConsentDialog(event: UserListViewEvents.OnPoliciesRetrieved) { + requireContext().showIdentityServerConsentDialog( + event.identityServerWithTerms, + consentCallBack = { viewModel.handle(UserListAction.UpdateUserConsent(true)) } + ) } override fun onUseQRCode() { diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewEvents.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewEvents.kt index f5ec044597..ffda0dd505 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewEvents.kt @@ -17,10 +17,13 @@ package im.vector.app.features.userdirectory import im.vector.app.core.platform.VectorViewEvents +import im.vector.app.features.discovery.IdentityServerWithTerms /** * Transient events for invite users to room screen */ sealed class UserListViewEvents : VectorViewEvents { + data class Failure(val throwable: Throwable) : UserListViewEvents() + data class OnPoliciesRetrieved(val identityServerWithTerms: IdentityServerWithTerms?) : UserListViewEvents() data class OpenShareMatrixToLink(val link: String) : UserListViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt index 0ef56084b7..61f8bc35f3 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt @@ -23,12 +23,15 @@ import com.airbnb.mvrx.Uninitialized import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.R import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.toggle import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.discovery.fetchIdentityServerWithTerms import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.filter @@ -36,6 +39,7 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.sample +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.Session @@ -51,9 +55,11 @@ data class ThreePidUser( val user: User? ) -class UserListViewModel @AssistedInject constructor(@Assisted initialState: UserListViewState, - private val session: Session) : - VectorViewModel(initialState) { +class UserListViewModel @AssistedInject constructor( + @Assisted initialState: UserListViewState, + private val stringProvider: StringProvider, + private val session: Session +) : VectorViewModel(initialState) { private val knownUsersSearch = MutableStateFlow("") private val directoryUsersSearch = MutableStateFlow("") @@ -104,11 +110,24 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User is UserListAction.AddPendingSelection -> handleSelectUser(action) is UserListAction.RemovePendingSelection -> handleRemoveSelectedUser(action) UserListAction.ComputeMatrixToLinkForSharing -> handleShareMyMatrixToLink() + UserListAction.UserConsentRequest -> handleUserConsentRequest() is UserListAction.UpdateUserConsent -> handleISUpdateConsent(action) UserListAction.Resumed -> handleResumed() }.exhaustive } + private fun handleUserConsentRequest() { + viewModelScope.launch { + val event = try { + val result = session.fetchIdentityServerWithTerms(stringProvider.getString(R.string.resources_language)) + UserListViewEvents.OnPoliciesRetrieved(result) + } catch (throwable: Throwable) { + UserListViewEvents.Failure(throwable) + } + _viewEvents.post(event) + } + } + private fun handleISUpdateConsent(action: UserListAction.UpdateUserConsent) { session.identityService().setUserConsent(action.consent) withState { From 9d6ac08a185e7f991cd1026b526e64ce77e528d4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 Nov 2021 17:46:20 +0100 Subject: [PATCH 3/3] Move to their own file since those class are used at different places now --- .../discovery/DiscoverySettingsState.kt | 7 ----- .../discovery/IdentityServerWithTerms.kt | 27 +++++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/discovery/IdentityServerWithTerms.kt diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt index 3f5be60f2e..da8603a437 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt @@ -29,10 +29,3 @@ data class DiscoverySettingsState( val userConsent: Boolean = false, val isIdentityPolicyUrlsExpanded: Boolean = false ) : MavericksState - -data class IdentityServerWithTerms( - val serverUrl: String, - val policies: List -) - -data class IdentityServerPolicy(val name: String, val url: String) diff --git a/vector/src/main/java/im/vector/app/features/discovery/IdentityServerWithTerms.kt b/vector/src/main/java/im/vector/app/features/discovery/IdentityServerWithTerms.kt new file mode 100644 index 0000000000..36bae0d0c5 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/discovery/IdentityServerWithTerms.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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.discovery + +data class IdentityServerWithTerms( + val serverUrl: String, + val policies: List +) + +data class IdentityServerPolicy( + val name: String, + val url: String +)