Create room screen - Better navigation pattern

This commit is contained in:
Benoit Marty 2019-06-10 07:57:32 +02:00
parent 4c5bffe0f5
commit aa95ce3d02
8 changed files with 99 additions and 17 deletions

View file

@ -0,0 +1,38 @@
/*
* 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.riotredesign.core.mvrx
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.airbnb.mvrx.MvRxState
import im.vector.riotredesign.core.platform.VectorViewModel
import im.vector.riotredesign.core.utils.LiveEvent
// MvRx require a state with at least one attribute
data class NavigationState(val dummy: Boolean = false) : MvRxState
abstract class NavigationViewModel<NavigationClass>(initialState: NavigationState) : VectorViewModel<NavigationState>(initialState) {
private val _navigateTo = MutableLiveData<LiveEvent<NavigationClass>>()
val navigateTo: LiveData<LiveEvent<NavigationClass>>
get() = _navigateTo
fun goTo(navigation: NavigationClass) {
_navigateTo.postValue(LiveEvent(navigation))
}
}

View file

@ -40,6 +40,11 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var onTextChange: ((String) -> Unit)? = null var onTextChange: ((String) -> Unit)? = null
private val onTextChangeListener = object : SimpleTextWatcher() {
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
onTextChange?.invoke(s.toString())
}
}
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
holder.textInputLayout.isEnabled = enabled holder.textInputLayout.isEnabled = enabled
@ -51,11 +56,7 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
} }
holder.textInputEditText.isEnabled = enabled holder.textInputEditText.isEnabled = enabled
holder.textInputEditText.addTextChangedListener(object : SimpleTextWatcher() { holder.textInputEditText.addTextChangedListener(onTextChangeListener)
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
onTextChange?.invoke(s.toString())
}
})
} }
override fun shouldSaveViewState(): Boolean { override fun shouldSaveViewState(): Boolean {
@ -64,9 +65,7 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
override fun unbind(holder: Holder) { override fun unbind(holder: Holder) {
super.unbind(holder) super.unbind(holder)
holder.textInputEditText.removeTextChangedListener(onTextChangeListener)
// TODO Remove onTextChanged?
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {

View file

@ -49,6 +49,7 @@ import java.util.concurrent.TimeUnit
class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback { class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback {
private val viewModel: RoomDirectoryViewModel by activityViewModel() private val viewModel: RoomDirectoryViewModel by activityViewModel()
private val navigationViewModel: RoomDirectoryNavigationViewModel by activityViewModel()
private val publicRoomsController: PublicRoomsController by inject() private val publicRoomsController: PublicRoomsController by inject()
private val errorFormatter: ErrorFormatter by inject() private val errorFormatter: ErrorFormatter by inject()
@ -76,8 +77,7 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback
.disposeOnDestroy() .disposeOnDestroy()
publicRoomsCreateNewRoom.setOnClickListener { publicRoomsCreateNewRoom.setOnClickListener {
// TODO Not the best navigation pattern navigationViewModel.goTo(RoomDirectoryActivity.Navigation.CreateRoom)
(vectorBaseActivity as? RoomDirectoryActivity)?.gotoCreateRoom()
} }
viewModel.joinRoomErrorLiveData.observe(this, Observer { viewModel.joinRoomErrorLiveData.observe(this, Observer {

View file

@ -17,6 +17,8 @@
package im.vector.riotredesign.features.roomdirectory package im.vector.riotredesign.features.roomdirectory
import android.os.Bundle import android.os.Bundle
import androidx.lifecycle.Observer
import com.airbnb.mvrx.viewModel
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.extensions.addFragment import im.vector.riotredesign.core.extensions.addFragment
import im.vector.riotredesign.core.extensions.addFragmentToBackstack import im.vector.riotredesign.core.extensions.addFragmentToBackstack
@ -27,6 +29,14 @@ import org.koin.android.scope.ext.android.getOrCreateScope
class RoomDirectoryActivity : VectorBaseActivity() { class RoomDirectoryActivity : VectorBaseActivity() {
// Supported navigation actions for this Activity
sealed class Navigation {
object Back : Navigation()
object CreateRoom : Navigation()
}
private val navigationViewModel: RoomDirectoryNavigationViewModel by viewModel()
override fun getLayoutRes() = R.layout.activity_simple override fun getLayoutRes() = R.layout.activity_simple
@ -34,6 +44,13 @@ class RoomDirectoryActivity : VectorBaseActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE)) bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
navigationViewModel.navigateTo.observe(this, Observer { liveEvent ->
when (liveEvent.getContentIfNotHandled() ?: return@Observer) {
is Navigation.Back -> onBackPressed()
is Navigation.CreateRoom -> gotoCreateRoom()
}
})
} }
override fun initUiAndData() { override fun initUiAndData() {
@ -42,8 +59,7 @@ class RoomDirectoryActivity : VectorBaseActivity() {
} }
} }
private fun gotoCreateRoom() {
fun gotoCreateRoom() {
addFragmentToBackstack(CreateRoomFragment(), R.id.simpleFragmentContainer) addFragmentToBackstack(CreateRoomFragment(), R.id.simpleFragmentContainer)
} }
} }

View file

@ -0,0 +1,23 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.roomdirectory
import im.vector.riotredesign.core.mvrx.NavigationState
import im.vector.riotredesign.core.mvrx.NavigationViewModel
class RoomDirectoryNavigationViewModel(initialState: NavigationState)
: NavigationViewModel<RoomDirectoryActivity.Navigation>(initialState)

View file

@ -20,11 +20,14 @@ import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.platform.VectorBaseFragment import im.vector.riotredesign.core.platform.VectorBaseFragment
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryActivity
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryNavigationViewModel
import kotlinx.android.synthetic.main.fragment_create_room.* import kotlinx.android.synthetic.main.fragment_create_room.*
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.android.scope.ext.android.bindScope import org.koin.android.scope.ext.android.bindScope
@ -33,6 +36,7 @@ import timber.log.Timber
class CreateRoomFragment : VectorBaseFragment(), CreateRoomController.Listener { class CreateRoomFragment : VectorBaseFragment(), CreateRoomController.Listener {
private val navigationViewModel: RoomDirectoryNavigationViewModel by activityViewModel()
private val viewModel: CreateRoomViewModel by fragmentViewModel() private val viewModel: CreateRoomViewModel by fragmentViewModel()
private val createRoomController: CreateRoomController by inject() private val createRoomController: CreateRoomController by inject()
@ -49,8 +53,7 @@ class CreateRoomFragment : VectorBaseFragment(), CreateRoomController.Listener {
setupRecyclerView() setupRecyclerView()
createRoomClose.setOnClickListener { createRoomClose.setOnClickListener {
// TODO Not the best way to manage Fragment Backstack... navigationViewModel.goTo(RoomDirectoryActivity.Navigation.Back)
vectorBaseActivity.onBackPressed()
} }
} }

View file

@ -40,7 +40,7 @@ class CreateRoomViewModel(initialState: CreateRoomViewState,
fun setName(newName: String) = setState { copy(roomName = newName) } fun setName(newName: String) = setState { copy(roomName = newName) }
fun setIsPublic(value: Boolean) = setState { copy(isPublic = value) } fun setIsPublic(isPublic: Boolean) = setState { copy(isPublic = isPublic) }
fun setIsInRoomDirectory(isInRoomDirectory: Boolean) = setState { copy(isInRoomDirectory = isInRoomDirectory) } fun setIsInRoomDirectory(isInRoomDirectory: Boolean) = setState { copy(isInRoomDirectory = isInRoomDirectory) }
@ -59,6 +59,7 @@ class CreateRoomViewModel(initialState: CreateRoomViewState,
// Directory visibility // Directory visibility
visibility = if (state.isInRoomDirectory) RoomDirectoryVisibility.PUBLIC else RoomDirectoryVisibility.PRIVATE visibility = if (state.isInRoomDirectory) RoomDirectoryVisibility.PUBLIC else RoomDirectoryVisibility.PRIVATE
// Public room
preset = if (state.isPublic) CreateRoomPreset.PRESET_PUBLIC_CHAT else CreateRoomPreset.PRESET_PRIVATE_CHAT preset = if (state.isPublic) CreateRoomPreset.PRESET_PUBLIC_CHAT else CreateRoomPreset.PRESET_PRIVATE_CHAT
} }

View file

@ -26,7 +26,9 @@ import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.platform.VectorBaseFragment import im.vector.riotredesign.core.platform.VectorBaseFragment
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryActivity
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryNavigationViewModel
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel
import kotlinx.android.synthetic.main.fragment_room_directory_picker.* import kotlinx.android.synthetic.main.fragment_room_directory_picker.*
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -39,6 +41,7 @@ import timber.log.Timber
class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerController.Callback { class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerController.Callback {
private val viewModel: RoomDirectoryViewModel by activityViewModel() private val viewModel: RoomDirectoryViewModel by activityViewModel()
private val navigationViewModel: RoomDirectoryNavigationViewModel by activityViewModel()
private val pickerViewModel: RoomDirectoryPickerViewModel by fragmentViewModel() private val pickerViewModel: RoomDirectoryPickerViewModel by fragmentViewModel()
private val roomDirectoryPickerController: RoomDirectoryPickerController by inject() private val roomDirectoryPickerController: RoomDirectoryPickerController by inject()
@ -88,8 +91,7 @@ class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerCon
Timber.v("onRoomDirectoryClicked: $roomDirectoryData") Timber.v("onRoomDirectoryClicked: $roomDirectoryData")
viewModel.setRoomDirectoryData(roomDirectoryData) viewModel.setRoomDirectoryData(roomDirectoryData)
// TODO Not the best way to manage Fragment Backstack... navigationViewModel.goTo(RoomDirectoryActivity.Navigation.Back)
vectorBaseActivity.onBackPressed()
} }
override fun retry() { override fun retry() {