Merge branch 'develop' into feature/version_name

This commit is contained in:
Benoit Marty 2020-01-22 11:45:27 +01:00 committed by GitHub
commit dca950140d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 877 additions and 237 deletions

View File

@ -0,0 +1,35 @@
A full developer contributors list can be found [here](https://github.com/vector-im/riotX-android/graphs/contributors).
# Core team:
Even if we try to be able to work on all the functionalities, we have more knowledge about what we have developed ourselves.
## Benoit: Android team leader
[@benoit.marty:matrix.org](https://matrix.to/#/@benoit.marty:matrix.org)
- Android team leader and project leader, Android developer, GitHub community manager.
- Specialist of the account creation, and many other fun features.
- Reviewing and polishing developed features, code quality manager, PRs reviewer, GitHub community manager.
- Release manager on the Play Store
## François: Software architect
[@ganfra:matrix.org](https://matrix.to/#/@ganfra:matrix.org)
- Software architect, Android developer
- First developer on the project.
- Work mainly on the global architecture of the project.
- Specialist of the timeline, and lots of other features.
## Valere: Product manager, Android developer
[@valere35:matrix.org](https://matrix.to/#/@valere35:matrix.org)
- Product manager, Android developer
- Specialist on the crypto implementation.
# Other contributors
First of all, we thank all contributors who use RiotX and report problems on this GitHub project or via the integrated rageshake function.
We do not forget all translators, for their work of translating RiotX into many languages. They are also the authors of RiotX.
Feel free to add your name below, when you contribute to the project!

View File

@ -2,7 +2,7 @@ Changes in RiotX 0.14.0 (2020-XX-XX)
=================================================== ===================================================
Features ✨: Features ✨:
- - Enable encryption in unencrypted rooms, from the room settings (#212)
Improvements 🙌: Improvements 🙌:
- -
@ -17,6 +17,7 @@ Translations 🗣:
- -
Build 🧱: Build 🧱:
- Ensure builds are reproducible (#842)
- F-Droid: fix the "-dev" issue in version name (#815) - F-Droid: fix the "-dev" issue in version name (#815)
Changes in RiotX 0.13.0 (2020-01-17) Changes in RiotX 0.13.0 (2020-01-17)

View File

@ -74,7 +74,7 @@ android {
} }
static def gitRevision() { static def gitRevision() {
def cmd = "git rev-parse --short HEAD" def cmd = "git rev-parse --short=8 HEAD"
return cmd.execute().text.trim() return cmd.execute().text.trim()
} }

View File

@ -45,7 +45,7 @@ def getVersionCode() {
} }
static def gitRevision() { static def gitRevision() {
def cmd = "git rev-parse --short HEAD" def cmd = "git rev-parse --short=8 HEAD"
return cmd.execute().text.trim() return cmd.execute().text.trim()
} }

View File

@ -22,32 +22,49 @@ import androidx.fragment.app.FragmentFactory
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
import dagger.multibindings.IntoMap import dagger.multibindings.IntoMap
import im.vector.riotx.features.createdirect.CreateDirectRoomDirectoryUsersFragment
import im.vector.riotx.features.createdirect.CreateDirectRoomKnownUsersFragment
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
import im.vector.riotx.features.crypto.verification.SASVerificationIncomingFragment import im.vector.riotx.features.crypto.verification.SASVerificationIncomingFragment
import im.vector.riotx.features.crypto.verification.SASVerificationShortCodeFragment import im.vector.riotx.features.crypto.verification.SASVerificationShortCodeFragment
import im.vector.riotx.features.crypto.verification.SASVerificationStartFragment import im.vector.riotx.features.crypto.verification.SASVerificationStartFragment
import im.vector.riotx.features.crypto.verification.SASVerificationVerifiedFragment import im.vector.riotx.features.crypto.verification.SASVerificationVerifiedFragment
import im.vector.riotx.features.grouplist.GroupListFragment
import im.vector.riotx.features.home.HomeDetailFragment import im.vector.riotx.features.home.HomeDetailFragment
import im.vector.riotx.features.home.HomeDrawerFragment import im.vector.riotx.features.home.HomeDrawerFragment
import im.vector.riotx.features.home.LoadingFragment import im.vector.riotx.features.home.LoadingFragment
import im.vector.riotx.features.createdirect.CreateDirectRoomDirectoryUsersFragment
import im.vector.riotx.features.createdirect.CreateDirectRoomKnownUsersFragment
import im.vector.riotx.features.grouplist.GroupListFragment
import im.vector.riotx.features.home.room.breadcrumbs.BreadcrumbsFragment import im.vector.riotx.features.home.room.breadcrumbs.BreadcrumbsFragment
import im.vector.riotx.features.home.room.detail.RoomDetailFragment import im.vector.riotx.features.home.room.detail.RoomDetailFragment
import im.vector.riotx.features.home.room.list.RoomListFragment import im.vector.riotx.features.home.room.list.RoomListFragment
import im.vector.riotx.features.login.* import im.vector.riotx.features.login.LoginCaptchaFragment
import im.vector.riotx.features.login.LoginFragment
import im.vector.riotx.features.login.LoginGenericTextInputFormFragment
import im.vector.riotx.features.login.LoginResetPasswordFragment
import im.vector.riotx.features.login.LoginResetPasswordMailConfirmationFragment
import im.vector.riotx.features.login.LoginResetPasswordSuccessFragment
import im.vector.riotx.features.login.LoginServerSelectionFragment
import im.vector.riotx.features.login.LoginServerUrlFormFragment
import im.vector.riotx.features.login.LoginSignUpSignInSelectionFragment
import im.vector.riotx.features.login.LoginSplashFragment
import im.vector.riotx.features.login.LoginWaitForEmailFragment
import im.vector.riotx.features.login.LoginWebFragment
import im.vector.riotx.features.login.terms.LoginTermsFragment import im.vector.riotx.features.login.terms.LoginTermsFragment
import im.vector.riotx.features.roommemberprofile.RoomMemberProfileFragment
import im.vector.riotx.features.reactions.EmojiChooserFragment import im.vector.riotx.features.reactions.EmojiChooserFragment
import im.vector.riotx.features.reactions.EmojiSearchResultFragment import im.vector.riotx.features.reactions.EmojiSearchResultFragment
import im.vector.riotx.features.roomdirectory.PublicRoomsFragment import im.vector.riotx.features.roomdirectory.PublicRoomsFragment
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment
import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
import im.vector.riotx.features.roommemberprofile.RoomMemberProfileFragment
import im.vector.riotx.features.roomprofile.RoomProfileFragment import im.vector.riotx.features.roomprofile.RoomProfileFragment
import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment
import im.vector.riotx.features.settings.* import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment
import im.vector.riotx.features.settings.VectorSettingsAdvancedNotificationPreferenceFragment
import im.vector.riotx.features.settings.VectorSettingsHelpAboutFragment
import im.vector.riotx.features.settings.VectorSettingsNotificationPreferenceFragment
import im.vector.riotx.features.settings.VectorSettingsNotificationsTroubleshootFragment
import im.vector.riotx.features.settings.VectorSettingsPreferencesFragment
import im.vector.riotx.features.settings.VectorSettingsSecurityPrivacyFragment
import im.vector.riotx.features.settings.devices.VectorSettingsDevicesFragment import im.vector.riotx.features.settings.devices.VectorSettingsDevicesFragment
import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment
import im.vector.riotx.features.settings.push.PushGatewaysFragment import im.vector.riotx.features.settings.push.PushGatewaysFragment
@ -272,6 +289,11 @@ interface FragmentModule {
@FragmentKey(RoomMemberListFragment::class) @FragmentKey(RoomMemberListFragment::class)
fun bindRoomMemberListFragment(fragment: RoomMemberListFragment): Fragment fun bindRoomMemberListFragment(fragment: RoomMemberListFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomSettingsFragment::class)
fun bindRoomSettingsFragment(fragment: RoomSettingsFragment): Fragment
@Binds @Binds
@IntoMap @IntoMap
@FragmentKey(RoomMemberProfileFragment::class) @FragmentKey(RoomMemberProfileFragment::class)

View File

@ -43,11 +43,14 @@ abstract class ProfileActionItem : VectorEpoxyModel<ProfileActionItem.Holder>()
@EpoxyAttribute @EpoxyAttribute
var destructive: Boolean = false var destructive: Boolean = false
@EpoxyAttribute @EpoxyAttribute
lateinit var listener: View.OnClickListener var listener: View.OnClickListener? = null
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.view.setOnClickListener(listener) holder.view.setOnClickListener(listener)
if (listener == null) {
holder.view.isClickable = false
}
holder.editable.isVisible = editable holder.editable.isVisible = editable
holder.title.text = title holder.title.text = title
val tintColor = if (destructive) { val tintColor = if (destructive) {

View File

@ -19,6 +19,7 @@ package im.vector.riotx.core.epoxy.profiles
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import im.vector.riotx.core.epoxy.ClickListener
import im.vector.riotx.core.epoxy.dividerItem import im.vector.riotx.core.epoxy.dividerItem
fun EpoxyController.buildProfileSection(title: String) { fun EpoxyController.buildProfileSection(title: String) {
@ -37,7 +38,7 @@ fun EpoxyController.buildProfileAction(
@DrawableRes icon: Int = 0, @DrawableRes icon: Int = 0,
destructive: Boolean = false, destructive: Boolean = false,
divider: Boolean = true, divider: Boolean = true,
action: () -> Unit action: ClickListener? = null
) { ) {
profileActionItem { profileActionItem {
iconRes(icon) iconRes(icon)
@ -47,7 +48,7 @@ fun EpoxyController.buildProfileAction(
destructive(destructive) destructive(destructive)
title(title) title(title)
listener { _ -> listener { _ ->
action() action?.invoke()
} }
} }

View File

@ -0,0 +1,20 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.core.extensions
// Trick to ensure that when block is exhaustive
val <T> T.exhaustive: T get() = this

View File

@ -22,10 +22,15 @@ import android.app.ProgressDialog
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.* import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.annotation.MainThread import androidx.annotation.MainThread
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.ViewModelProviders
@ -35,11 +40,13 @@ import com.airbnb.mvrx.BaseMvRxFragment
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import com.bumptech.glide.util.Util.assertMainThread import com.bumptech.glide.util.Util.assertMainThread
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import im.vector.riotx.R
import im.vector.riotx.core.di.DaggerScreenComponent import im.vector.riotx.core.di.DaggerScreenComponent
import im.vector.riotx.core.di.HasScreenInjector import im.vector.riotx.core.di.HasScreenInjector
import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.error.ErrorFormatter import im.vector.riotx.core.error.ErrorFormatter
import im.vector.riotx.features.navigation.Navigator import im.vector.riotx.features.navigation.Navigator
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import timber.log.Timber import timber.log.Timber
@ -120,6 +127,14 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
mUnBinder = ButterKnife.bind(this, view) mUnBinder = ButterKnife.bind(this, view)
} }
open fun showLoading(message: CharSequence?) {
showLoadingDialog(message)
}
open fun showFailure(throwable: Throwable) {
displayErrorDialog(throwable)
}
@CallSuper @CallSuper
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
@ -182,10 +197,10 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
} }
} }
protected fun showLoadingDialog(message: CharSequence, cancelable: Boolean = false) { protected fun showLoadingDialog(message: CharSequence? = null, cancelable: Boolean = false) {
progress = ProgressDialog(requireContext()).apply { progress = ProgressDialog(requireContext()).apply {
setCancelable(cancelable) setCancelable(cancelable)
setMessage(message) setMessage(message ?: getString(R.string.please_wait))
setProgressStyle(ProgressDialog.STYLE_SPINNER) setProgressStyle(ProgressDialog.STYLE_SPINNER)
show() show()
} }
@ -220,6 +235,21 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
return this return this
} }
/* ==========================================================================================
* ViewEvents
* ========================================================================================== */
protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(observer: (T) -> Unit) {
viewEvents
.observe()
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
dismissLoadingDialog()
observer(it)
}
.disposeOnDestroyView()
}
/* ========================================================================================== /* ==========================================================================================
* MENU MANAGEMENT * MENU MANAGEMENT
* ========================================================================================== */ * ========================================================================================== */
@ -233,4 +263,16 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
inflater.inflate(menuRes, menu) inflater.inflate(menuRes, menu)
} }
} }
/* ==========================================================================================
* Common Dialogs
* ========================================================================================== */
protected fun displayErrorDialog(throwable: Throwable) {
AlertDialog.Builder(requireActivity())
.setTitle(R.string.dialog_title_error)
.setMessage(errorFormatter.toHumanReadable(throwable))
.setPositiveButton(R.string.ok, null)
.show()
}
} }

View File

@ -0,0 +1,27 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.core.platform
/**
* Interface for View Events
*/
interface VectorViewEvents
/**
* To use when no view events is associated to the ViewModel
*/
object EmptyViewEvents : VectorViewEvents

View File

@ -16,20 +16,23 @@
package im.vector.riotx.core.platform package im.vector.riotx.core.platform
import androidx.lifecycle.LiveData import com.airbnb.mvrx.Async
import androidx.lifecycle.MutableLiveData import com.airbnb.mvrx.BaseMvRxViewModel
import com.airbnb.mvrx.* import com.airbnb.mvrx.Fail
import im.vector.riotx.core.utils.LiveEvent import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Success
import im.vector.riotx.core.utils.DataSource
import im.vector.riotx.core.utils.PublishDataSource
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
abstract class VectorViewModel<S : MvRxState, A : VectorViewModelAction>(initialState: S) abstract class VectorViewModel<S : MvRxState, VA : VectorViewModelAction, VE : VectorViewEvents>(initialState: S)
: BaseMvRxViewModel<S>(initialState, false) { : BaseMvRxViewModel<S>(initialState, false) {
// Generic handling of any request error // Used to post transient events to the View
protected val _requestErrorLiveData = MutableLiveData<LiveEvent<Throwable>>() protected val _viewEvents = PublishDataSource<VE>()
val requestErrorLiveData: LiveData<LiveEvent<Throwable>> val viewEvents: DataSource<VE> = _viewEvents
get() = _requestErrorLiveData
/** /**
* This method does the same thing as the execute function, but it doesn't subscribe to the stream * This method does the same thing as the execute function, but it doesn't subscribe to the stream
@ -53,5 +56,5 @@ abstract class VectorViewModel<S : MvRxState, A : VectorViewModelAction>(initial
.doOnNext { setState { stateReducer(it) } } .doOnNext { setState { stateReducer(it) } }
} }
abstract fun handle(action: A) abstract fun handle(action: VA)
} }

View File

@ -23,7 +23,11 @@ import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.textChanges import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.* import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.extensions.hideKeyboard
import im.vector.riotx.core.extensions.setupAsSearch
import im.vector.riotx.core.extensions.showKeyboard
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_create_direct_room_directory_users.* import kotlinx.android.synthetic.main.fragment_create_direct_room_directory_users.*
import javax.inject.Inject import javax.inject.Inject

View File

@ -33,6 +33,7 @@ import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.toMatrixItem import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.utils.LiveEvent import im.vector.riotx.core.utils.LiveEvent
import io.reactivex.Single import io.reactivex.Single
@ -51,7 +52,7 @@ data class SelectUserAction(
class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
initialState: CreateDirectRoomViewState, initialState: CreateDirectRoomViewState,
private val session: Session) private val session: Session)
: VectorViewModel<CreateDirectRoomViewState, CreateDirectRoomAction>(initialState) { : VectorViewModel<CreateDirectRoomViewState, CreateDirectRoomAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -24,11 +24,12 @@ import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialState: KeysBackupSettingViewState, class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialState: KeysBackupSettingViewState,
session: Session session: Session
) : VectorViewModel<KeysBackupSettingViewState, KeyBackupSettingsAction>(initialState), ) : VectorViewModel<KeysBackupSettingViewState, KeyBackupSettingsAction, EmptyViewEvents>(initialState),
KeysBackupStateListener { KeysBackupStateListener {
@AssistedInject.Factory @AssistedInject.Factory

View File

@ -33,6 +33,7 @@ import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.utils.LiveEvent import im.vector.riotx.core.utils.LiveEvent
@ -45,7 +46,7 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
private val selectedGroupStore: SelectedGroupDataSource, private val selectedGroupStore: SelectedGroupDataSource,
private val session: Session, private val session: Session,
private val stringProvider: StringProvider private val stringProvider: StringProvider
) : VectorViewModel<GroupListViewState, GroupListAction>(initialState) { ) : VectorViewModel<GroupListViewState, GroupListAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -24,6 +24,7 @@ import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.di.HasScreenInjector import im.vector.riotx.core.di.HasScreenInjector
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.grouplist.SelectedGroupDataSource import im.vector.riotx.features.grouplist.SelectedGroupDataSource
@ -40,7 +41,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
private val selectedGroupStore: SelectedGroupDataSource, private val selectedGroupStore: SelectedGroupDataSource,
private val homeRoomListStore: HomeRoomListDataSource, private val homeRoomListStore: HomeRoomListDataSource,
private val stringProvider: StringProvider) private val stringProvider: StringProvider)
: VectorViewModel<HomeDetailViewState, HomeDetailAction>(initialState) { : VectorViewModel<HomeDetailViewState, HomeDetailAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -24,12 +24,13 @@ import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.platform.EmptyAction import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: BreadcrumbsViewState, class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: BreadcrumbsViewState,
private val session: Session) private val session: Session)
: VectorViewModel<BreadcrumbsViewState, EmptyAction>(initialState) { : VectorViewModel<BreadcrumbsViewState, EmptyAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -85,6 +85,7 @@ import im.vector.riotx.R
import im.vector.riotx.core.dialogs.withColoredButton import im.vector.riotx.core.dialogs.withColoredButton
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.extensions.hideKeyboard import im.vector.riotx.core.extensions.hideKeyboard
import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.extensions.setTextOrHide
@ -306,15 +307,11 @@ class RoomDetailFragment @Inject constructor(
displayRoomDetailActionResult(it) displayRoomDetailActionResult(it)
} }
roomDetailViewModel.viewEvents roomDetailViewModel.observeViewEvents {
.observe() when (it) {
.observeOn(AndroidSchedulers.mainThread()) is RoomDetailViewEvents.Failure -> showErrorInSnackbar(it.throwable)
.subscribe { }.exhaustive
when (it) { }
is RoomDetailViewEvents.Failure -> showErrorInSnackbar(it.throwable)
}
}
.disposeOnDestroyView()
} }
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {

View File

@ -16,9 +16,11 @@
package im.vector.riotx.features.home.room.detail package im.vector.riotx.features.home.room.detail
import im.vector.riotx.core.platform.VectorViewEvents
/** /**
* Transient events for RoomDetail * Transient events for RoomDetail
*/ */
sealed class RoomDetailViewEvents { sealed class RoomDetailViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : RoomDetailViewEvents() data class Failure(val throwable: Throwable) : RoomDetailViewEvents()
} }

View File

@ -20,7 +20,12 @@ import android.net.Uri
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.airbnb.mvrx.* import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
import com.jakewharton.rxrelay2.BehaviorRelay import com.jakewharton.rxrelay2.BehaviorRelay
import com.jakewharton.rxrelay2.PublishRelay import com.jakewharton.rxrelay2.PublishRelay
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
@ -55,9 +60,7 @@ import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.resources.UserPreferencesProvider import im.vector.riotx.core.resources.UserPreferencesProvider
import im.vector.riotx.core.utils.DataSource
import im.vector.riotx.core.utils.LiveEvent import im.vector.riotx.core.utils.LiveEvent
import im.vector.riotx.core.utils.PublishDataSource
import im.vector.riotx.core.utils.subscribeLogError import im.vector.riotx.core.utils.subscribeLogError
import im.vector.riotx.features.command.CommandParser import im.vector.riotx.features.command.CommandParser
import im.vector.riotx.features.command.ParsedCommand import im.vector.riotx.features.command.ParsedCommand
@ -81,7 +84,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val typingHelper: TypingHelper, private val typingHelper: TypingHelper,
private val session: Session private val session: Session
) : VectorViewModel<RoomDetailViewState, RoomDetailAction>(initialState), Timeline.Listener { ) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState), Timeline.Listener {
private val room = session.getRoom(initialState.roomId)!! private val room = session.getRoom(initialState.roomId)!!
private val eventId = initialState.eventId private val eventId = initialState.eventId
@ -104,9 +107,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
var timeline = room.createTimeline(eventId, timelineSettings) var timeline = room.createTimeline(eventId, timelineSettings)
private set private set
private val _viewEvents = PublishDataSource<RoomDetailViewEvents>()
val viewEvents: DataSource<RoomDetailViewEvents> = _viewEvents
// Can be used for several actions, for a one shot result // Can be used for several actions, for a one shot result
private val _requestLiveData = MutableLiveData<LiveEvent<Async<RoomDetailAction>>>() private val _requestLiveData = MutableLiveData<LiveEvent<Async<RoomDetailAction>>>()
val requestLiveData: LiveData<LiveEvent<Async<RoomDetailAction>>> val requestLiveData: LiveData<LiveEvent<Async<RoomDetailAction>>>
@ -290,6 +290,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
} }
} }
// TODO Cleanup this and use ViewEvents
private val _nonBlockingPopAlert = MutableLiveData<LiveEvent<Pair<Int, List<Any>>>>() private val _nonBlockingPopAlert = MutableLiveData<LiveEvent<Pair<Int, List<Any>>>>()
val nonBlockingPopAlert: LiveData<LiveEvent<Pair<Int, List<Any>>>> val nonBlockingPopAlert: LiveData<LiveEvent<Pair<Int, List<Any>>>>
get() = _nonBlockingPopAlert get() = _nonBlockingPopAlert

View File

@ -35,6 +35,7 @@ import im.vector.matrix.rx.rx
import im.vector.matrix.rx.unwrap import im.vector.matrix.rx.unwrap
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.canReact import im.vector.riotx.core.extensions.canReact
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.home.room.detail.timeline.format.NoticeEventFormatter import im.vector.riotx.features.home.room.detail.timeline.format.NoticeEventFormatter
@ -89,7 +90,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
private val noticeEventFormatter: NoticeEventFormatter, private val noticeEventFormatter: NoticeEventFormatter,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val vectorPreferences: VectorPreferences private val vectorPreferences: VectorPreferences
) : VectorViewModel<MessageActionState, MessageActionsAction>(initialState) { ) : VectorViewModel<MessageActionState, MessageActionsAction, EmptyViewEvents>(initialState) {
private val eventId = initialState.eventId private val eventId = initialState.eventId
private val informationData = initialState.informationData private val informationData = initialState.informationData

View File

@ -15,7 +15,15 @@
*/ */
package im.vector.riotx.features.home.room.detail.timeline.edithistory package im.vector.riotx.features.home.room.detail.timeline.edithistory
import com.airbnb.mvrx.* import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxState
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.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
@ -28,10 +36,11 @@ import im.vector.matrix.android.api.session.room.model.message.isReply
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
import im.vector.riotx.core.date.VectorDateFormatter import im.vector.riotx.core.date.VectorDateFormatter
import im.vector.riotx.core.platform.EmptyAction import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.UUID
data class ViewEditHistoryViewState( data class ViewEditHistoryViewState(
val eventId: String, val eventId: String,
@ -47,7 +56,7 @@ class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
initialState: ViewEditHistoryViewState, initialState: ViewEditHistoryViewState,
val session: Session, val session: Session,
val dateFormatter: VectorDateFormatter val dateFormatter: VectorDateFormatter
) : VectorViewModel<ViewEditHistoryViewState, EmptyAction>(initialState) { ) : VectorViewModel<ViewEditHistoryViewState, EmptyAction, EmptyViewEvents>(initialState) {
private val roomId = initialState.roomId private val roomId = initialState.roomId
private val eventId = initialState.eventId private val eventId = initialState.eventId

View File

@ -16,7 +16,12 @@
package im.vector.riotx.features.home.room.detail.timeline.reactions package im.vector.riotx.features.home.room.detail.timeline.reactions
import com.airbnb.mvrx.* import com.airbnb.mvrx.Async
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
@ -25,6 +30,7 @@ import im.vector.matrix.rx.RxRoom
import im.vector.matrix.rx.unwrap import im.vector.matrix.rx.unwrap
import im.vector.riotx.core.date.VectorDateFormatter import im.vector.riotx.core.date.VectorDateFormatter
import im.vector.riotx.core.platform.EmptyAction import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
import io.reactivex.Observable import io.reactivex.Observable
@ -54,7 +60,7 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted
initialState: DisplayReactionsViewState, initialState: DisplayReactionsViewState,
private val session: Session, private val session: Session,
private val dateFormatter: VectorDateFormatter private val dateFormatter: VectorDateFormatter
) : VectorViewModel<DisplayReactionsViewState, EmptyAction>(initialState) { ) : VectorViewModel<DisplayReactionsViewState, EmptyAction, EmptyViewEvents>(initialState) {
private val roomId = initialState.roomId private val roomId = initialState.roomId
private val eventId = initialState.eventId private val eventId = initialState.eventId

View File

@ -27,7 +27,11 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.airbnb.epoxy.OnModelBuildFinishedListener import com.airbnb.epoxy.OnModelBuildFinishedListener
import com.airbnb.mvrx.* import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
@ -35,6 +39,7 @@ import im.vector.matrix.android.api.session.room.notification.RoomNotificationSt
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.OnBackPressed import im.vector.riotx.core.platform.OnBackPressed
import im.vector.riotx.core.platform.StateView import im.vector.riotx.core.platform.StateView
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
@ -46,7 +51,6 @@ import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsShare
import im.vector.riotx.features.home.room.list.widget.FabMenuView import im.vector.riotx.features.home.room.list.widget.FabMenuView
import im.vector.riotx.features.notifications.NotificationDrawerManager import im.vector.riotx.features.notifications.NotificationDrawerManager
import im.vector.riotx.features.share.SharedData import im.vector.riotx.features.share.SharedData
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_room_list.* import kotlinx.android.synthetic.main.fragment_room_list.*
import javax.inject.Inject import javax.inject.Inject
@ -98,16 +102,13 @@ class RoomListFragment @Inject constructor(
setupRecyclerView() setupRecyclerView()
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
roomListViewModel.subscribe { renderState(it) } roomListViewModel.subscribe { renderState(it) }
roomListViewModel.viewEvents roomListViewModel.observeViewEvents {
.observe() when (it) {
.observeOn(AndroidSchedulers.mainThread()) is RoomListViewEvents.Loading -> showLoading(it.message)
.subscribe { is RoomListViewEvents.Failure -> showFailure(it.throwable)
when (it) { is RoomListViewEvents.SelectRoom -> openSelectedRoom(it)
is RoomListViewEvents.SelectRoom -> openSelectedRoom(it) }.exhaustive
is RoomListViewEvents.Failure -> showErrorInSnackbar(it.throwable) }
}
}
.disposeOnDestroyView()
createChatFabMenu.listener = this createChatFabMenu.listener = this
@ -117,6 +118,10 @@ class RoomListFragment @Inject constructor(
.disposeOnDestroyView() .disposeOnDestroyView()
} }
override fun showFailure(throwable: Throwable) {
showErrorInSnackbar(throwable)
}
override fun onDestroyView() { override fun onDestroyView() {
roomController.removeModelBuildListener(modelBuildListener) roomController.removeModelBuildListener(modelBuildListener)
modelBuildListener = null modelBuildListener = null

View File

@ -17,10 +17,14 @@
package im.vector.riotx.features.home.room.list package im.vector.riotx.features.home.room.list
import im.vector.riotx.core.platform.VectorViewEvents
/** /**
* Transient events for RoomList * Transient events for RoomList
*/ */
sealed class RoomListViewEvents { sealed class RoomListViewEvents : VectorViewEvents {
data class Loading(val message: CharSequence? = null) : RoomListViewEvents()
data class Failure(val throwable: Throwable) : RoomListViewEvents() data class Failure(val throwable: Throwable) : RoomListViewEvents()
data class SelectRoom(val roomId: String) : RoomListViewEvents() data class SelectRoom(val roomId: String) : RoomListViewEvents()
} }

View File

@ -26,7 +26,6 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.tag.RoomTag import im.vector.matrix.android.api.session.room.model.tag.RoomTag
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.utils.DataSource import im.vector.riotx.core.utils.DataSource
import im.vector.riotx.core.utils.PublishDataSource
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -34,7 +33,7 @@ import javax.inject.Inject
class RoomListViewModel @Inject constructor(initialState: RoomListViewState, class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
private val session: Session, private val session: Session,
private val roomSummariesSource: DataSource<List<RoomSummary>>) private val roomSummariesSource: DataSource<List<RoomSummary>>)
: VectorViewModel<RoomListViewState, RoomListAction>(initialState) { : VectorViewModel<RoomListViewState, RoomListAction, RoomListViewEvents>(initialState) {
interface Factory { interface Factory {
fun create(initialState: RoomListViewState): RoomListViewModel fun create(initialState: RoomListViewState): RoomListViewModel
@ -52,9 +51,6 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
private val displayMode = initialState.displayMode private val displayMode = initialState.displayMode
private val roomListDisplayModeFilter = RoomListDisplayModeFilter(displayMode) private val roomListDisplayModeFilter = RoomListDisplayModeFilter(displayMode)
private val _viewEvents = PublishDataSource<RoomListViewEvents>()
val viewEvents: DataSource<RoomListViewEvents> = _viewEvents
init { init {
observeRoomSummaries() observeRoomSummaries()
} }
@ -197,6 +193,7 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
} }
private fun handleLeaveRoom(action: RoomListAction.LeaveRoom) { private fun handleLeaveRoom(action: RoomListAction.LeaveRoom) {
_viewEvents.post(RoomListViewEvents.Loading(null))
session.getRoom(action.roomId)?.leave(null, object : MatrixCallback<Unit> { session.getRoom(action.roomId)?.leave(null, object : MatrixCallback<Unit> {
override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
_viewEvents.post(RoomListViewEvents.Failure(failure)) _viewEvents.post(RoomListViewEvents.Failure(failure))

View File

@ -24,11 +24,12 @@ import im.vector.matrix.android.api.session.Session
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.matrix.rx.unwrap import im.vector.matrix.rx.unwrap
import im.vector.riotx.core.platform.EmptyAction import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initialState: RoomListQuickActionsState, class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initialState: RoomListQuickActionsState,
session: Session session: Session
) : VectorViewModel<RoomListQuickActionsState, EmptyAction>(initialState) { ) : VectorViewModel<RoomListQuickActionsState, EmptyAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -27,9 +27,9 @@ import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.failure.MatrixError import im.vector.matrix.android.api.failure.MatrixError
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.OnBackPressed import im.vector.riotx.core.platform.OnBackPressed
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import io.reactivex.android.schedulers.AndroidSchedulers
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
/** /**
@ -59,25 +59,21 @@ abstract class AbstractLoginFragment : VectorBaseFragment(), OnBackPressed {
loginSharedActionViewModel = activityViewModelProvider.get(LoginSharedActionViewModel::class.java) loginSharedActionViewModel = activityViewModelProvider.get(LoginSharedActionViewModel::class.java)
loginViewModel.viewEvents loginViewModel.observeViewEvents {
.observe() handleLoginViewEvents(it)
.observeOn(AndroidSchedulers.mainThread()) }
.subscribe {
handleLoginViewEvents(it)
}
.disposeOnDestroyView()
} }
private fun handleLoginViewEvents(loginViewEvents: LoginViewEvents) { private fun handleLoginViewEvents(loginViewEvents: LoginViewEvents) {
when (loginViewEvents) { when (loginViewEvents) {
is LoginViewEvents.Error -> showError(loginViewEvents.throwable) is LoginViewEvents.Failure -> showFailure(loginViewEvents.throwable)
else -> else ->
// This is handled by the Activity // This is handled by the Activity
Unit Unit
} }.exhaustive
} }
private fun showError(throwable: Throwable) { override fun showFailure(throwable: Throwable) {
when (throwable) { when (throwable) {
is Failure.ServerError -> { is Failure.ServerError -> {
if (throwable.error.code == MatrixError.M_FORBIDDEN if (throwable.error.code == MatrixError.M_FORBIDDEN
@ -96,11 +92,7 @@ abstract class AbstractLoginFragment : VectorBaseFragment(), OnBackPressed {
} }
open fun onError(throwable: Throwable) { open fun onError(throwable: Throwable) {
AlertDialog.Builder(requireActivity()) super.showFailure(throwable)
.setTitle(R.string.dialog_title_error)
.setMessage(errorFormatter.toHumanReadable(throwable))
.setPositiveButton(R.string.ok, null)
.show()
} }
override fun onBackPressed(toolbarButton: Boolean): Boolean { override fun onBackPressed(toolbarButton: Boolean): Boolean {

View File

@ -209,7 +209,7 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
.setMessage(R.string.login_error_outdated_homeserver_content) .setMessage(R.string.login_error_outdated_homeserver_content)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show() .show()
is LoginViewEvents.Error -> is LoginViewEvents.Failure ->
// This is handled by the Fragments // This is handled by the Fragments
Unit Unit
} }

View File

@ -18,12 +18,15 @@
package im.vector.riotx.features.login package im.vector.riotx.features.login
import im.vector.matrix.android.api.auth.registration.FlowResult import im.vector.matrix.android.api.auth.registration.FlowResult
import im.vector.riotx.core.platform.VectorViewEvents
/** /**
* Transient events for Login * Transient events for Login
*/ */
sealed class LoginViewEvents { sealed class LoginViewEvents: VectorViewEvents {
data class Loading(val message: CharSequence? = null) : LoginViewEvents()
data class Failure(val throwable: Throwable) : LoginViewEvents()
data class RegistrationFlowResult(val flowResult: FlowResult, val isRegistrationStarted: Boolean) : LoginViewEvents() data class RegistrationFlowResult(val flowResult: FlowResult, val isRegistrationStarted: Boolean) : LoginViewEvents()
data class Error(val throwable: Throwable) : LoginViewEvents()
object OutdatedHomeserver : LoginViewEvents() object OutdatedHomeserver : LoginViewEvents()
} }

View File

@ -41,8 +41,6 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.extensions.configureAndStart import im.vector.riotx.core.extensions.configureAndStart
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.utils.DataSource
import im.vector.riotx.core.utils.PublishDataSource
import im.vector.riotx.features.notifications.PushRuleTriggerListener import im.vector.riotx.features.notifications.PushRuleTriggerListener
import im.vector.riotx.features.session.SessionListener import im.vector.riotx.features.session.SessionListener
import im.vector.riotx.features.signout.soft.SoftLogoutActivity import im.vector.riotx.features.signout.soft.SoftLogoutActivity
@ -59,7 +57,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
private val pushRuleTriggerListener: PushRuleTriggerListener, private val pushRuleTriggerListener: PushRuleTriggerListener,
private val homeServerConnectionConfigFactory: HomeServerConnectionConfigFactory, private val homeServerConnectionConfigFactory: HomeServerConnectionConfigFactory,
private val sessionListener: SessionListener) private val sessionListener: SessionListener)
: VectorViewModel<LoginViewState, LoginAction>(initialState) { : VectorViewModel<LoginViewState, LoginAction, LoginViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -95,9 +93,6 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
private var currentTask: Cancelable? = null private var currentTask: Cancelable? = null
private val _viewEvents = PublishDataSource<LoginViewEvents>()
val viewEvents: DataSource<LoginViewEvents> = _viewEvents
override fun handle(action: LoginAction) { override fun handle(action: LoginAction) {
when (action) { when (action) {
is LoginAction.UpdateServerType -> handleUpdateServerType(action) is LoginAction.UpdateServerType -> handleUpdateServerType(action)
@ -179,7 +174,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
if (failure !is CancellationException) { if (failure !is CancellationException) {
_viewEvents.post(LoginViewEvents.Error(failure)) _viewEvents.post(LoginViewEvents.Failure(failure))
} }
setState { setState {
copy( copy(
@ -201,7 +196,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
} }
override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
_viewEvents.post(LoginViewEvents.Error(failure)) _viewEvents.post(LoginViewEvents.Failure(failure))
setState { setState {
copy( copy(
asyncRegistration = Uninitialized asyncRegistration = Uninitialized
@ -223,7 +218,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
} }
override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
_viewEvents.post(LoginViewEvents.Error(failure)) _viewEvents.post(LoginViewEvents.Failure(failure))
setState { setState {
copy( copy(
asyncRegistration = Uninitialized asyncRegistration = Uninitialized
@ -526,7 +521,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
if (homeServerConnectionConfig == null) { if (homeServerConnectionConfig == null) {
// This is invalid // This is invalid
_viewEvents.post(LoginViewEvents.Error(Throwable("Unable to create a HomeServerConnectionConfig"))) _viewEvents.post(LoginViewEvents.Failure(Throwable("Unable to create a HomeServerConnectionConfig")))
} else { } else {
currentTask?.cancel() currentTask?.cancel()
currentTask = null currentTask = null
@ -540,7 +535,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
currentTask = authenticationService.getLoginFlow(homeServerConnectionConfig, object : MatrixCallback<LoginFlowResult> { currentTask = authenticationService.getLoginFlow(homeServerConnectionConfig, object : MatrixCallback<LoginFlowResult> {
override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
_viewEvents.post(LoginViewEvents.Error(failure)) _viewEvents.post(LoginViewEvents.Failure(failure))
setState { setState {
copy( copy(
asyncHomeServerLoginFlowRequest = Uninitialized asyncHomeServerLoginFlowRequest = Uninitialized

View File

@ -21,6 +21,7 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.reactions.data.EmojiDataSource import im.vector.riotx.features.reactions.data.EmojiDataSource
import im.vector.riotx.features.reactions.data.EmojiItem import im.vector.riotx.features.reactions.data.EmojiItem
@ -33,7 +34,7 @@ data class EmojiSearchResultViewState(
class EmojiSearchResultViewModel @AssistedInject constructor( class EmojiSearchResultViewModel @AssistedInject constructor(
@Assisted initialState: EmojiSearchResultViewState, @Assisted initialState: EmojiSearchResultViewState,
private val dataSource: EmojiDataSource) private val dataSource: EmojiDataSource)
: VectorViewModel<EmojiSearchResultViewState, EmojiSearchAction>(initialState) { : VectorViewModel<EmojiSearchResultViewState, EmojiSearchAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -75,6 +75,7 @@ class PublicRoomsFragment @Inject constructor(
sharedActionViewModel.post(RoomDirectorySharedAction.CreateRoom) sharedActionViewModel.post(RoomDirectorySharedAction.CreateRoom)
} }
// TODO remove this, replace by ViewEvents
viewModel.joinRoomErrorLiveData.observeEvent(this) { throwable -> viewModel.joinRoomErrorLiveData.observeEvent(this) { throwable ->
Snackbar.make(publicRoomsCoordinator, errorFormatter.toHumanReadable(throwable), Snackbar.LENGTH_SHORT) Snackbar.make(publicRoomsCoordinator, errorFormatter.toHumanReadable(throwable), Snackbar.LENGTH_SHORT)
.show() .show()

View File

@ -33,6 +33,7 @@ import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.utils.LiveEvent import im.vector.riotx.core.utils.LiveEvent
import timber.log.Timber import timber.log.Timber
@ -41,7 +42,7 @@ private const val PUBLIC_ROOMS_LIMIT = 20
class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: PublicRoomsViewState, class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: PublicRoomsViewState,
private val session: Session) private val session: Session)
: VectorViewModel<PublicRoomsViewState, RoomDirectoryAction>(initialState) { : VectorViewModel<PublicRoomsViewState, RoomDirectoryAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -17,7 +17,12 @@
package im.vector.riotx.features.roomdirectory.createroom package im.vector.riotx.features.roomdirectory.createroom
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.airbnb.mvrx.* import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.Fail
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.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
@ -25,12 +30,13 @@ import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.room.model.create.CreateRoomPreset import im.vector.matrix.android.api.session.room.model.create.CreateRoomPreset
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: CreateRoomViewState, class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: CreateRoomViewState,
private val session: Session private val session: Session
) : VectorViewModel<CreateRoomViewState, CreateRoomAction>(initialState) { ) : VectorViewModel<CreateRoomViewState, CreateRoomAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -16,17 +16,22 @@
package im.vector.riotx.features.roomdirectory.picker package im.vector.riotx.features.roomdirectory.picker
import com.airbnb.mvrx.* import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext
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.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initialState: RoomDirectoryPickerViewState, class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initialState: RoomDirectoryPickerViewState,
private val session: Session) private val session: Session)
: VectorViewModel<RoomDirectoryPickerViewState, RoomDirectoryPickerAction>(initialState) { : VectorViewModel<RoomDirectoryPickerViewState, RoomDirectoryPickerAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -26,13 +26,14 @@ import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.roomSummaryQueryParams import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.roomdirectory.JoinState import im.vector.riotx.features.roomdirectory.JoinState
import timber.log.Timber import timber.log.Timber
class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: RoomPreviewViewState, class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: RoomPreviewViewState,
private val session: Session) private val session: Session)
: VectorViewModel<RoomPreviewViewState, RoomPreviewAction>(initialState) { : VectorViewModel<RoomPreviewViewState, RoomPreviewAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -20,13 +20,19 @@ package im.vector.riotx.features.roommemberprofile
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.View import android.view.View
import com.airbnb.mvrx.* import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.util.MatrixItem import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.animations.AppBarStateChangeListener import im.vector.riotx.core.animations.AppBarStateChangeListener
import im.vector.riotx.core.animations.MatrixItemAppBarStateChangeListener import im.vector.riotx.core.animations.MatrixItemAppBarStateChangeListener
import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.extensions.setTextOrHide
import im.vector.riotx.core.platform.StateView import im.vector.riotx.core.platform.StateView
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
@ -73,16 +79,13 @@ class RoomMemberProfileFragment @Inject constructor(
appBarStateChangeListener = MatrixItemAppBarStateChangeListener(headerView, listOf(matrixProfileToolbarAvatarImageView, appBarStateChangeListener = MatrixItemAppBarStateChangeListener(headerView, listOf(matrixProfileToolbarAvatarImageView,
matrixProfileToolbarTitleView)) matrixProfileToolbarTitleView))
matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener) matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
viewModel.viewEvents viewModel.observeViewEvents {
.observe() when (it) {
.subscribe { is RoomMemberProfileViewEvents.Loading -> showLoading(it.message)
dismissLoadingDialog() is RoomMemberProfileViewEvents.Failure -> showFailure(it.throwable)
when (it) { is RoomMemberProfileViewEvents.OnIgnoreActionSuccess -> Unit
is RoomMemberProfileViewEvents.Loading -> showLoadingDialog(it.message) }.exhaustive
is RoomMemberProfileViewEvents.Failure -> showErrorInSnackbar(it.throwable) }
}
}
.disposeOnDestroyView()
} }
override fun onDestroyView() { override fun onDestroyView() {

View File

@ -16,11 +16,14 @@
package im.vector.riotx.features.roommemberprofile package im.vector.riotx.features.roommemberprofile
import im.vector.riotx.core.platform.VectorViewEvents
/** /**
* Transient events for RoomMemberProfile * Transient events for RoomMemberProfile
*/ */
sealed class RoomMemberProfileViewEvents { sealed class RoomMemberProfileViewEvents : VectorViewEvents {
data class Loading(val message: CharSequence) : RoomMemberProfileViewEvents() data class Loading(val message: CharSequence? = null) : RoomMemberProfileViewEvents()
object OnIgnoreActionSuccess : RoomMemberProfileViewEvents()
data class Failure(val throwable: Throwable) : RoomMemberProfileViewEvents() data class Failure(val throwable: Throwable) : RoomMemberProfileViewEvents()
object OnIgnoreActionSuccess : RoomMemberProfileViewEvents()
} }

View File

@ -44,8 +44,6 @@ import im.vector.matrix.rx.unwrap
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.utils.DataSource
import im.vector.riotx.core.utils.PublishDataSource
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.functions.BiFunction import io.reactivex.functions.BiFunction
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -55,7 +53,7 @@ import kotlinx.coroutines.withContext
class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState, class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val session: Session) private val session: Session)
: VectorViewModel<RoomMemberProfileViewState, RoomMemberProfileAction>(initialState) { : VectorViewModel<RoomMemberProfileViewState, RoomMemberProfileAction, RoomMemberProfileViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -71,9 +69,6 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
} }
} }
private val _viewEvents = PublishDataSource<RoomMemberProfileViewEvents>()
val viewEvents: DataSource<RoomMemberProfileViewEvents> = _viewEvents
private val room = if (initialState.roomId != null) { private val room = if (initialState.roomId != null) {
session.getRoom(initialState.roomId) session.getRoom(initialState.roomId)
} else { } else {
@ -183,7 +178,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
private fun handleIgnoreAction() = withState { state -> private fun handleIgnoreAction() = withState { state ->
val isIgnored = state.isIgnored() ?: return@withState val isIgnored = state.isIgnored() ?: return@withState
_viewEvents.post(RoomMemberProfileViewEvents.Loading(stringProvider.getString(R.string.please_wait))) _viewEvents.post(RoomMemberProfileViewEvents.Loading())
val ignoreActionCallback = object : MatrixCallback<Unit> { val ignoreActionCallback = object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
_viewEvents.post(RoomMemberProfileViewEvents.OnIgnoreActionSuccess) _viewEvents.post(RoomMemberProfileViewEvents.OnIgnoreActionSuccess)

View File

@ -26,6 +26,7 @@ import im.vector.riotx.core.extensions.addFragmentToBackstack
import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.ToolbarConfigurable
import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment
import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment
class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable { class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable {
@ -69,7 +70,7 @@ class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable {
} }
private fun openRoomSettings() { private fun openRoomSettings() {
notImplemented("Open room settings") addFragmentToBackstack(R.id.simpleFragmentContainer, RoomSettingsFragment::class.java, roomProfileArgs)
} }
private fun openRoomMembers() { private fun openRoomMembers() {

View File

@ -31,6 +31,7 @@ import im.vector.riotx.core.animations.AppBarStateChangeListener
import im.vector.riotx.core.animations.MatrixItemAppBarStateChangeListener import im.vector.riotx.core.animations.MatrixItemAppBarStateChangeListener
import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.extensions.setTextOrHide
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
@ -77,17 +78,13 @@ class RoomProfileFragment @Inject constructor(
appBarStateChangeListener = MatrixItemAppBarStateChangeListener(headerView, listOf(matrixProfileToolbarAvatarImageView, appBarStateChangeListener = MatrixItemAppBarStateChangeListener(headerView, listOf(matrixProfileToolbarAvatarImageView,
matrixProfileToolbarTitleView)) matrixProfileToolbarTitleView))
matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener) matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
roomProfileViewModel.viewEvents roomProfileViewModel.observeViewEvents {
.observe() when (it) {
.subscribe { is RoomProfileViewEvents.Loading -> showLoading(it.message)
dismissLoadingDialog() is RoomProfileViewEvents.Failure -> showFailure(it.throwable)
when (it) { is RoomProfileViewEvents.OnLeaveRoomSuccess -> onLeaveRoom()
is RoomProfileViewEvents.Loading -> showLoadingDialog(it.message) }.exhaustive
RoomProfileViewEvents.OnLeaveRoomSuccess -> onLeaveRoom() }
is RoomProfileViewEvents.Failure -> showError(it.throwable)
}
}
.disposeOnDestroyView()
roomListQuickActionsSharedActionViewModel roomListQuickActionsSharedActionViewModel
.observe() .observe()
.subscribe { handleQuickActions(it) } .subscribe { handleQuickActions(it) }

View File

@ -15,11 +15,15 @@
*/ */
package im.vector.riotx.features.roomprofile package im.vector.riotx.features.roomprofile
import im.vector.riotx.core.platform.VectorViewEvents
/** /**
* Transient events for RoomProfile * Transient events for RoomProfile
*/ */
sealed class RoomProfileViewEvents { sealed class RoomProfileViewEvents : VectorViewEvents {
data class Loading(val message: CharSequence): RoomProfileViewEvents() data class Loading(val message: CharSequence? = null) : RoomProfileViewEvents()
object OnLeaveRoomSuccess: RoomProfileViewEvents()
data class Failure(val throwable: Throwable) : RoomProfileViewEvents() data class Failure(val throwable: Throwable) : RoomProfileViewEvents()
object OnLeaveRoomSuccess : RoomProfileViewEvents()
} }

View File

@ -29,13 +29,11 @@ import im.vector.matrix.rx.unwrap
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.utils.DataSource
import im.vector.riotx.core.utils.PublishDataSource
class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: RoomProfileViewState, class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: RoomProfileViewState,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val session: Session) private val session: Session)
: VectorViewModel<RoomProfileViewState, RoomProfileAction>(initialState) { : VectorViewModel<RoomProfileViewState, RoomProfileAction, RoomProfileViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -51,9 +49,6 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R
} }
} }
private val _viewEvents = PublishDataSource<RoomProfileViewEvents>()
val viewEvents: DataSource<RoomProfileViewEvents> = _viewEvents
private val room = session.getRoom(initialState.roomId)!! private val room = session.getRoom(initialState.roomId)!!
init { init {

View File

@ -29,7 +29,7 @@ import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.roomprofile.RoomProfileArgs import im.vector.riotx.features.roomprofile.RoomProfileArgs
import kotlinx.android.synthetic.main.fragment_room_member_list.* import kotlinx.android.synthetic.main.fragment_room_setting_generic.*
import javax.inject.Inject import javax.inject.Inject
class RoomMemberListFragment @Inject constructor( class RoomMemberListFragment @Inject constructor(
@ -41,12 +41,12 @@ class RoomMemberListFragment @Inject constructor(
private val viewModel: RoomMemberListViewModel by fragmentViewModel() private val viewModel: RoomMemberListViewModel by fragmentViewModel()
private val roomProfileArgs: RoomProfileArgs by args() private val roomProfileArgs: RoomProfileArgs by args()
override fun getLayoutResId() = R.layout.fragment_room_member_list override fun getLayoutResId() = R.layout.fragment_room_setting_generic
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
roomMemberListController.callback = this roomMemberListController.callback = this
setupToolbar(roomMemberListToolbar) setupToolbar(roomSettingsToolbar)
recyclerView.configureWith(roomMemberListController, hasFixedSize = true) recyclerView.configureWith(roomMemberListController, hasFixedSize = true)
} }
@ -66,8 +66,8 @@ class RoomMemberListFragment @Inject constructor(
private fun renderRoomSummary(state: RoomMemberListViewState) { private fun renderRoomSummary(state: RoomMemberListViewState) {
state.roomSummary()?.let { state.roomSummary()?.let {
roomMemberListToolbarTitleView.text = it.displayName roomSettingsToolbarTitleView.text = it.displayName
avatarRenderer.render(it.toMatrixItem(), roomMemberListToolbarAvatarImageView) avatarRenderer.render(it.toMatrixItem(), roomSettingsToolbarAvatarImageView)
} }
} }
} }

View File

@ -34,13 +34,14 @@ import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper
import im.vector.matrix.rx.mapOptional import im.vector.matrix.rx.mapOptional
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.matrix.rx.unwrap import im.vector.matrix.rx.unwrap
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.functions.BiFunction import io.reactivex.functions.BiFunction
class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState, class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState,
private val session: Session) private val session: Session)
: VectorViewModel<RoomMemberListViewState, RoomMemberListAction>(initialState) { : VectorViewModel<RoomMemberListViewState, RoomMemberListAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -0,0 +1,26 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.roomprofile.settings
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class RoomSettingsAction : VectorViewModelAction {
data class SetRoomName(val newName: String) : RoomSettingsAction()
data class SetRoomTopic(val newTopic: String) : RoomSettingsAction()
data class SetRoomAvatar(val newAvatarUrl: String) : RoomSettingsAction()
object EnableEncryption : RoomSettingsAction()
}

View File

@ -0,0 +1,72 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.roomprofile.settings
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.profiles.buildProfileAction
import im.vector.riotx.core.epoxy.profiles.buildProfileSection
import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.resources.StringProvider
import javax.inject.Inject
// TODO Add other feature here (waiting for design)
class RoomSettingsController @Inject constructor(
private val stringProvider: StringProvider,
colorProvider: ColorProvider
) : TypedEpoxyController<RoomSettingsViewState>() {
interface Callback {
fun onEnableEncryptionClicked()
}
private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color)
var callback: Callback? = null
init {
setData(null)
}
override fun buildModels(data: RoomSettingsViewState?) {
val roomSummary = data?.roomSummary?.invoke() ?: return
buildProfileSection(
stringProvider.getString(R.string.settings)
)
if (roomSummary.isEncrypted) {
buildProfileAction(
id = "encryption",
title = stringProvider.getString(R.string.room_settings_addresses_e2e_enabled),
dividerColor = dividerColor,
divider = false,
editable = false
)
} else {
buildProfileAction(
id = "encryption",
title = stringProvider.getString(R.string.room_settings_enable_encryption),
subtitle = stringProvider.getString(R.string.room_settings_enable_encryption_warning),
dividerColor = dividerColor,
divider = false,
editable = true,
action = { callback?.onEnableEncryptionClicked() }
)
}
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.roomprofile.settings
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.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R
import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.roomprofile.RoomProfileArgs
import kotlinx.android.synthetic.main.fragment_room_setting_generic.*
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import javax.inject.Inject
class RoomSettingsFragment @Inject constructor(
val viewModelFactory: RoomSettingsViewModel.Factory,
private val controller: RoomSettingsController,
private val avatarRenderer: AvatarRenderer
) : VectorBaseFragment(), RoomSettingsController.Callback {
private val viewModel: RoomSettingsViewModel 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)
recyclerView.configureWith(controller, hasFixedSize = true)
waiting_view_status_text.setText(R.string.please_wait)
waiting_view_status_text.isVisible = true
viewModel.observeViewEvents {
when (it) {
is RoomSettingsViewEvents.Failure -> showFailure(it.throwable)
}.exhaustive
}
}
override fun onDestroyView() {
recyclerView.cleanup()
super.onDestroyView()
}
override fun invalidate() = withState(viewModel) { viewState ->
controller.setData(viewState)
renderRoomSummary(viewState)
}
override fun onEnableEncryptionClicked() {
AlertDialog.Builder(requireActivity())
.setTitle(R.string.room_settings_enable_encryption_dialog_title)
.setMessage(R.string.room_settings_enable_encryption_dialog_content)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.room_settings_enable_encryption_dialog_submit) { _, _ ->
viewModel.handle(RoomSettingsAction.EnableEncryption)
}
.show()
}
private fun renderRoomSummary(state: RoomSettingsViewState) {
waiting_view.isVisible = state.isLoading
state.roomSummary()?.let {
roomSettingsToolbarTitleView.text = it.displayName
avatarRenderer.render(it.toMatrixItem(), roomSettingsToolbarAvatarImageView)
}
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package im.vector.riotx.features.roomprofile.settings
import im.vector.riotx.core.platform.VectorViewEvents
/**
* Transient events for room settings screen
*/
sealed class RoomSettingsViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : RoomSettingsViewEvents()
}

View File

@ -0,0 +1,90 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.roomprofile.settings
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import im.vector.matrix.rx.rx
import im.vector.matrix.rx.unwrap
import im.vector.riotx.core.platform.VectorViewModel
class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: RoomSettingsViewState,
private val session: Session)
: VectorViewModel<RoomSettingsViewState, RoomSettingsAction, RoomSettingsViewEvents>(initialState) {
@AssistedInject.Factory
interface Factory {
fun create(initialState: RoomSettingsViewState): RoomSettingsViewModel
}
companion object : MvRxViewModelFactory<RoomSettingsViewModel, RoomSettingsViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: RoomSettingsViewState): RoomSettingsViewModel? {
val fragment: RoomSettingsFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.viewModelFactory.create(state)
}
}
private val room = session.getRoom(initialState.roomId)!!
init {
observeRoomSummary()
}
private fun observeRoomSummary() {
room.rx().liveRoomSummary()
.unwrap()
.execute { async ->
copy(roomSummary = async)
}
}
override fun handle(action: RoomSettingsAction) {
when (action) {
is RoomSettingsAction.EnableEncryption -> handleEnableEncryption()
}
}
private fun handleEnableEncryption() {
setState {
copy(isLoading = true)
}
room.enableEncryption(MXCRYPTO_ALGORITHM_MEGOLM, object : MatrixCallback<Unit> {
override fun onFailure(failure: Throwable) {
setState {
copy(isLoading = false)
}
_viewEvents.post(RoomSettingsViewEvents.Failure(failure))
}
override fun onSuccess(data: Unit) {
setState {
copy(isLoading = false)
}
}
})
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.roomprofile.settings
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotx.features.roomprofile.RoomProfileArgs
data class RoomSettingsViewState(
val roomId: String,
val roomSummary: Async<RoomSummary> = Uninitialized,
val isLoading: Boolean = false
) : MvRxState {
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
}

View File

@ -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.riotx.features.settings.devices
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class DevicesAction : VectorViewModelAction {
object Retry : DevicesAction()
data class Delete(val deviceInfo: DeviceInfo) : DevicesAction()
data class Password(val password: String) : DevicesAction()
data class Rename(val deviceInfo: DeviceInfo, val newName: String) : DevicesAction()
data class ToggleDevice(val deviceInfo: DeviceInfo) : DevicesAction()
}

View File

@ -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.riotx.features.settings.devices
import im.vector.riotx.core.platform.VectorViewEvents
/**
* Transient events for Ignored users screen
*/
sealed class DevicesViewEvents : VectorViewEvents {
data class Loading(val message: CharSequence? = null) : DevicesViewEvents()
data class Failure(val throwable: Throwable) : DevicesViewEvents()
}

View File

@ -18,7 +18,15 @@ package im.vector.riotx.features.settings.devices
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.airbnb.mvrx.* import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxState
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.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
@ -29,7 +37,6 @@ import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.platform.VectorViewModelAction
import im.vector.riotx.core.utils.LiveEvent import im.vector.riotx.core.utils.LiveEvent
import timber.log.Timber import timber.log.Timber
@ -37,20 +44,13 @@ data class DevicesViewState(
val myDeviceId: String = "", val myDeviceId: String = "",
val devices: Async<List<DeviceInfo>> = Uninitialized, val devices: Async<List<DeviceInfo>> = Uninitialized,
val currentExpandedDeviceId: String? = null, val currentExpandedDeviceId: String? = null,
// TODO Replace by isLoading boolean
val request: Async<Unit> = Uninitialized val request: Async<Unit> = Uninitialized
) : MvRxState ) : MvRxState
sealed class DevicesAction : VectorViewModelAction {
object Retry : DevicesAction()
data class Delete(val deviceInfo: DeviceInfo) : DevicesAction()
data class Password(val password: String) : DevicesAction()
data class Rename(val deviceInfo: DeviceInfo, val newName: String) : DevicesAction()
data class ToggleDevice(val deviceInfo: DeviceInfo) : DevicesAction()
}
class DevicesViewModel @AssistedInject constructor(@Assisted initialState: DevicesViewState, class DevicesViewModel @AssistedInject constructor(@Assisted initialState: DevicesViewState,
private val session: Session) private val session: Session)
: VectorViewModel<DevicesViewState, DevicesAction>(initialState) { : VectorViewModel<DevicesViewState, DevicesAction, DevicesViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -153,7 +153,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
) )
} }
_requestErrorLiveData.postLiveEvent(failure) _viewEvents.post(DevicesViewEvents.Failure(failure))
} }
}) })
} }
@ -207,7 +207,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
) )
} }
_requestErrorLiveData.postLiveEvent(failure) _viewEvents.post(DevicesViewEvents.Failure(failure))
} }
} }
@ -261,7 +261,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
) )
} }
_requestErrorLiveData.postLiveEvent(failure) _viewEvents.post(DevicesViewEvents.Failure(failure))
} }
}) })
} }

View File

@ -31,6 +31,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
@ -52,7 +53,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
override fun getLayoutResId() = R.layout.fragment_generic_recycler override fun getLayoutResId() = R.layout.fragment_generic_recycler
private val devicesViewModel: DevicesViewModel by fragmentViewModel() private val viewModel: DevicesViewModel by fragmentViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -61,16 +62,24 @@ class VectorSettingsDevicesFragment @Inject constructor(
waiting_view_status_text.isVisible = true waiting_view_status_text.isVisible = true
devicesController.callback = this devicesController.callback = this
recyclerView.configureWith(devicesController, showDivider = true) recyclerView.configureWith(devicesController, showDivider = true)
devicesViewModel.requestErrorLiveData.observeEvent(this) { viewModel.observeViewEvents {
displayErrorDialog(it) when (it) {
// Password is maybe not good, for safety measure, reset it here is DevicesViewEvents.Loading -> showLoading(it.message)
mAccountPassword = "" is DevicesViewEvents.Failure -> showFailure(it.throwable)
}.exhaustive
} }
devicesViewModel.requestPasswordLiveData.observeEvent(this) { viewModel.requestPasswordLiveData.observeEvent(this) {
maybeShowDeleteDeviceWithPasswordDialog() maybeShowDeleteDeviceWithPasswordDialog()
} }
} }
override fun showFailure(throwable: Throwable) {
super.showFailure(throwable)
// Password is maybe not good, for safety measure, reset it here
mAccountPassword = ""
}
override fun onDestroyView() { override fun onDestroyView() {
devicesController.callback = null devicesController.callback = null
recyclerView.cleanup() recyclerView.cleanup()
@ -83,20 +92,12 @@ class VectorSettingsDevicesFragment @Inject constructor(
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_devices_list) (activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_devices_list)
} }
private fun displayErrorDialog(throwable: Throwable) {
AlertDialog.Builder(requireActivity())
.setTitle(R.string.dialog_title_error)
.setMessage(errorFormatter.toHumanReadable(throwable))
.setPositiveButton(R.string.ok, null)
.show()
}
override fun onDeviceClicked(deviceInfo: DeviceInfo) { override fun onDeviceClicked(deviceInfo: DeviceInfo) {
devicesViewModel.handle(DevicesAction.ToggleDevice(deviceInfo)) viewModel.handle(DevicesAction.ToggleDevice(deviceInfo))
} }
override fun onDeleteDevice(deviceInfo: DeviceInfo) { override fun onDeleteDevice(deviceInfo: DeviceInfo) {
devicesViewModel.handle(DevicesAction.Delete(deviceInfo)) viewModel.handle(DevicesAction.Delete(deviceInfo))
} }
override fun onRenameDevice(deviceInfo: DeviceInfo) { override fun onRenameDevice(deviceInfo: DeviceInfo) {
@ -104,7 +105,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
} }
override fun retry() { override fun retry() {
devicesViewModel.handle(DevicesAction.Retry) viewModel.handle(DevicesAction.Retry)
} }
/** /**
@ -125,7 +126,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
.setPositiveButton(R.string.ok) { _, _ -> .setPositiveButton(R.string.ok) { _, _ ->
val newName = input.text.toString() val newName = input.text.toString()
devicesViewModel.handle(DevicesAction.Rename(deviceInfo, newName)) viewModel.handle(DevicesAction.Rename(deviceInfo, newName))
} }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.show() .show()
@ -136,7 +137,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
*/ */
private fun maybeShowDeleteDeviceWithPasswordDialog() { private fun maybeShowDeleteDeviceWithPasswordDialog() {
if (mAccountPassword.isNotEmpty()) { if (mAccountPassword.isNotEmpty()) {
devicesViewModel.handle(DevicesAction.Password(mAccountPassword)) viewModel.handle(DevicesAction.Password(mAccountPassword))
} else { } else {
val inflater = requireActivity().layoutInflater val inflater = requireActivity().layoutInflater
val layout = inflater.inflate(R.layout.dialog_device_delete, null) val layout = inflater.inflate(R.layout.dialog_device_delete, null)
@ -152,7 +153,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
return@OnClickListener return@OnClickListener
} }
mAccountPassword = passwordEditText.text.toString() mAccountPassword = passwordEditText.text.toString()
devicesViewModel.handle(DevicesAction.Password(mAccountPassword)) viewModel.handle(DevicesAction.Password(mAccountPassword))
}) })
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event -> .setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event ->
@ -166,7 +167,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
} }
} }
override fun invalidate() = withState(devicesViewModel) { state -> override fun invalidate() = withState(viewModel) { state ->
devicesController.update(state) devicesController.update(state)
handleRequestStatus(state.request) handleRequestStatus(state.request)

View File

@ -0,0 +1,28 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package im.vector.riotx.features.settings.ignored
import im.vector.riotx.core.platform.VectorViewEvents
/**
* Transient events for Ignored users screen
*/
sealed class IgnoredUsersViewEvents : VectorViewEvents {
data class Loading(val message: CharSequence? = null) : IgnoredUsersViewEvents()
data class Failure(val throwable: Throwable) : IgnoredUsersViewEvents()
}

View File

@ -16,14 +16,21 @@
package im.vector.riotx.features.settings.ignored package im.vector.riotx.features.settings.ignored
import com.airbnb.mvrx.* import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxState
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.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.platform.VectorViewModelAction import im.vector.riotx.core.platform.VectorViewModelAction
@ -38,7 +45,7 @@ sealed class IgnoredUsersAction : VectorViewModelAction {
class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: IgnoredUsersViewState, class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: IgnoredUsersViewState,
private val session: Session) private val session: Session)
: VectorViewModel<IgnoredUsersViewState, IgnoredUsersAction>(initialState) { : VectorViewModel<IgnoredUsersViewState, IgnoredUsersAction, IgnoredUsersViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -89,7 +96,7 @@ class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState:
) )
} }
_requestErrorLiveData.postLiveEvent(failure) _viewEvents.post(IgnoredUsersViewEvents.Failure(failure))
} }
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {

View File

@ -27,7 +27,7 @@ import com.airbnb.mvrx.withState
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_generic_recycler.* import kotlinx.android.synthetic.main.fragment_generic_recycler.*
@ -41,7 +41,7 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor(
override fun getLayoutResId() = R.layout.fragment_generic_recycler override fun getLayoutResId() = R.layout.fragment_generic_recycler
private val ignoredUsersViewModel: IgnoredUsersViewModel by fragmentViewModel() private val viewModel: IgnoredUsersViewModel by fragmentViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -50,8 +50,11 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor(
waiting_view_status_text.isVisible = true waiting_view_status_text.isVisible = true
ignoredUsersController.callback = this ignoredUsersController.callback = this
recyclerView.configureWith(ignoredUsersController) recyclerView.configureWith(ignoredUsersController)
ignoredUsersViewModel.requestErrorLiveData.observeEvent(this) { viewModel.observeViewEvents {
displayErrorDialog(it) when (it) {
is IgnoredUsersViewEvents.Loading -> showLoading(it.message)
is IgnoredUsersViewEvents.Failure -> showFailure(it.throwable)
}.exhaustive
} }
} }
@ -71,25 +74,17 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor(
AlertDialog.Builder(requireActivity()) AlertDialog.Builder(requireActivity())
.setMessage(getString(R.string.settings_unignore_user, userId)) .setMessage(getString(R.string.settings_unignore_user, userId))
.setPositiveButton(R.string.yes) { _, _ -> .setPositiveButton(R.string.yes) { _, _ ->
ignoredUsersViewModel.handle(IgnoredUsersAction.UnIgnore(userId)) viewModel.handle(IgnoredUsersAction.UnIgnore(userId))
} }
.setNegativeButton(R.string.no, null) .setNegativeButton(R.string.no, null)
.show() .show()
} }
private fun displayErrorDialog(throwable: Throwable) {
AlertDialog.Builder(requireActivity())
.setTitle(R.string.dialog_title_error)
.setMessage(errorFormatter.toHumanReadable(throwable))
.setPositiveButton(R.string.ok, null)
.show()
}
// ============================================================================================================== // ==============================================================================================================
// ignored users list management // ignored users list management
// ============================================================================================================== // ==============================================================================================================
override fun invalidate() = withState(ignoredUsersViewModel) { state -> override fun invalidate() = withState(viewModel) { state ->
ignoredUsersController.update(state) ignoredUsersController.update(state)
handleUnIgnoreRequestStatus(state.unIgnoreRequest) handleUnIgnoreRequestStatus(state.unIgnoreRequest)

View File

@ -16,13 +16,19 @@
package im.vector.riotx.features.settings.push package im.vector.riotx.features.settings.push
import com.airbnb.mvrx.* import com.airbnb.mvrx.Async
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.pushers.Pusher import im.vector.matrix.android.api.session.pushers.Pusher
import im.vector.matrix.rx.RxSession import im.vector.matrix.rx.RxSession
import im.vector.riotx.core.platform.EmptyAction import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
data class PushGatewayViewState( data class PushGatewayViewState(
@ -31,7 +37,7 @@ data class PushGatewayViewState(
class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: PushGatewayViewState, class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: PushGatewayViewState,
private val session: Session) private val session: Session)
: VectorViewModel<PushGatewayViewState, EmptyAction>(initialState) { : VectorViewModel<PushGatewayViewState, EmptyAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -21,6 +21,7 @@ import com.airbnb.mvrx.ViewModelContext
import im.vector.matrix.android.api.pushrules.rest.PushRule import im.vector.matrix.android.api.pushrules.rest.PushRule
import im.vector.riotx.core.di.HasScreenInjector import im.vector.riotx.core.di.HasScreenInjector
import im.vector.riotx.core.platform.EmptyAction import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
data class PushRulesViewState( data class PushRulesViewState(
@ -28,7 +29,7 @@ data class PushRulesViewState(
) : MvRxState ) : MvRxState
class PushRulesViewModel(initialState: PushRulesViewState) class PushRulesViewModel(initialState: PushRulesViewState)
: VectorViewModel<PushRulesViewState, EmptyAction>(initialState) { : VectorViewModel<PushRulesViewState, EmptyAction, EmptyViewEvents>(initialState) {
companion object : MvRxViewModelFactory<PushRulesViewModel, PushRulesViewState> { companion object : MvRxViewModelFactory<PushRulesViewModel, PushRulesViewState> {

View File

@ -26,6 +26,7 @@ import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.ActiveSessionDataSource import im.vector.riotx.ActiveSessionDataSource
import im.vector.riotx.core.platform.EmptyAction import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
@ -39,7 +40,7 @@ data class IncomingShareState(private val dummy: Boolean = false) : MvRxState
class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState: IncomingShareState, class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState: IncomingShareState,
private val sessionObservableStore: ActiveSessionDataSource, private val sessionObservableStore: ActiveSessionDataSource,
private val shareRoomListObservableStore: ShareRoomListDataSource) private val shareRoomListObservableStore: ShareRoomListDataSource)
: VectorViewModel<IncomingShareState, EmptyAction>(initialState) { : VectorViewModel<IncomingShareState, EmptyAction, EmptyViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {

View File

@ -73,7 +73,7 @@ class SoftLogoutActivity : LoginActivity() {
private fun handleSoftLogoutViewEvents(softLogoutViewEvents: SoftLogoutViewEvents) { private fun handleSoftLogoutViewEvents(softLogoutViewEvents: SoftLogoutViewEvents) {
when (softLogoutViewEvents) { when (softLogoutViewEvents) {
is SoftLogoutViewEvents.Error -> is SoftLogoutViewEvents.Failure ->
showError(errorFormatter.toHumanReadable(softLogoutViewEvents.throwable)) showError(errorFormatter.toHumanReadable(softLogoutViewEvents.throwable))
is SoftLogoutViewEvents.ErrorNotSameUser -> { is SoftLogoutViewEvents.ErrorNotSameUser -> {
// Pop the backstack // Pop the backstack

View File

@ -17,11 +17,14 @@
package im.vector.riotx.features.signout.soft package im.vector.riotx.features.signout.soft
import im.vector.riotx.core.platform.VectorViewEvents
/** /**
* Transient events for SoftLogout * Transient events for SoftLogout
*/ */
sealed class SoftLogoutViewEvents { sealed class SoftLogoutViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : SoftLogoutViewEvents()
data class ErrorNotSameUser(val currentUserId: String, val newUserId: String) : SoftLogoutViewEvents() data class ErrorNotSameUser(val currentUserId: String, val newUserId: String) : SoftLogoutViewEvents()
data class Error(val throwable: Throwable) : SoftLogoutViewEvents()
object ClearData : SoftLogoutViewEvents() object ClearData : SoftLogoutViewEvents()
} }

View File

@ -16,7 +16,13 @@
package im.vector.riotx.features.signout.soft package im.vector.riotx.features.signout.soft
import com.airbnb.mvrx.* import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.Fail
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.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
@ -28,8 +34,6 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.extensions.hasUnsavedKeys import im.vector.riotx.core.extensions.hasUnsavedKeys
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.utils.DataSource
import im.vector.riotx.core.utils.PublishDataSource
import im.vector.riotx.features.login.LoginMode import im.vector.riotx.features.login.LoginMode
import timber.log.Timber import timber.log.Timber
@ -41,7 +45,7 @@ class SoftLogoutViewModel @AssistedInject constructor(
private val session: Session, private val session: Session,
private val activeSessionHolder: ActiveSessionHolder, private val activeSessionHolder: ActiveSessionHolder,
private val authenticationService: AuthenticationService private val authenticationService: AuthenticationService
) : VectorViewModel<SoftLogoutViewState, SoftLogoutAction>(initialState) { ) : VectorViewModel<SoftLogoutViewState, SoftLogoutAction, SoftLogoutViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -71,9 +75,6 @@ class SoftLogoutViewModel @AssistedInject constructor(
private var currentTask: Cancelable? = null private var currentTask: Cancelable? = null
private val _viewEvents = PublishDataSource<SoftLogoutViewEvents>()
val viewEvents: DataSource<SoftLogoutViewEvents> = _viewEvents
init { init {
// Get the supported login flow // Get the supported login flow
getSupportedLoginFlow() getSupportedLoginFlow()
@ -192,7 +193,7 @@ class SoftLogoutViewModel @AssistedInject constructor(
currentTask = session.updateCredentials(action.credentials, currentTask = session.updateCredentials(action.credentials,
object : MatrixCallback<Unit> { object : MatrixCallback<Unit> {
override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
_viewEvents.post(SoftLogoutViewEvents.Error(failure)) _viewEvents.post(SoftLogoutViewEvents.Failure(failure))
setState { setState {
copy( copy(
asyncLoginAction = Uninitialized asyncLoginAction = Uninitialized

View File

@ -4,25 +4,26 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootConstraintLayout" android:id="@+id/rootConstraintLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:background="?riotx_header_panel_background">
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/roomMemberListToolbar" android:id="@+id/roomSettingsToolbar"
style="@style/VectorToolbarStyle" style="@style/VectorToolbarStyle"
android:elevation="4dp"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="?actionBarSize" android:layout_height="?actionBarSize"
android:elevation="4dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/roomMemberListToolbarContentView" android:id="@+id/roomSettingsToolbarContentView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<ImageView <ImageView
android:id="@+id/roomMemberListToolbarAvatarImageView" android:id="@+id/roomSettingsToolbarAvatarImageView"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
@ -33,7 +34,7 @@
tools:src="@tools:sample/avatars" /> tools:src="@tools:sample/avatars" />
<TextView <TextView
android:id="@+id/roomMemberListToolbarTitleView" android:id="@+id/roomSettingsToolbarTitleView"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
@ -42,9 +43,9 @@
android:maxLines="1" android:maxLines="1"
android:textColor="?vctr_toolbar_primary_text_color" android:textColor="?vctr_toolbar_primary_text_color"
android:textSize="18sp" android:textSize="18sp"
app:layout_constraintStart_toEndOf="@+id/roomMemberListToolbarAvatarImageView"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/roomSettingsToolbarAvatarImageView"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:text="@sample/matrix.json/data/roomName" /> tools:text="@sample/matrix.json/data/roomName" />
@ -57,10 +58,12 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:overScrollMode="always" android:overScrollMode="always"
app:layout_constraintTop_toBottomOf="@+id/roomMemberListToolbar" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@+id/roomSettingsToolbar"
tools:listitem="@layout/item_autocomplete_matrix_item" /> tools:listitem="@layout/item_profile_action" />
<include layout="@layout/merge_overlay_waiting_view" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -10,6 +10,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?vctr_waiting_background_color" android:background="?vctr_waiting_background_color"
android:clickable="true"
android:focusable="true"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"> tools:visibility="visible">

View File

@ -40,5 +40,11 @@
<!-- Title for category in the settings which affect the behavior of the message editor (ex: enable Markdown, send typing notification, etc.) --> <!-- Title for category in the settings which affect the behavior of the message editor (ex: enable Markdown, send typing notification, etc.) -->
<string name="settings_category_composer">Message editor</string> <string name="settings_category_composer">Message editor</string>
<string name="room_settings_enable_encryption">Enable end-to-end encryption</string>
<string name="room_settings_enable_encryption_warning">Once enabled, encryption cannot be disabled.</string>
<string name="room_settings_enable_encryption_dialog_title">Enable encryption?</string>
<string name="room_settings_enable_encryption_dialog_content">Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly.</string>
<string name="room_settings_enable_encryption_dialog_submit">Enable encryption</string>
</resources> </resources>