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
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) {
holder.textInputLayout.isEnabled = enabled
@ -51,11 +56,7 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
}
holder.textInputEditText.isEnabled = enabled

holder.textInputEditText.addTextChangedListener(object : SimpleTextWatcher() {
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
onTextChange?.invoke(s.toString())
}
})
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
}

override fun shouldSaveViewState(): Boolean {
@ -64,9 +65,7 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {

override fun unbind(holder: Holder) {
super.unbind(holder)

// TODO Remove onTextChanged?

holder.textInputEditText.removeTextChangedListener(onTextChangeListener)
}

class Holder : VectorEpoxyHolder() {

View File

@ -49,6 +49,7 @@ import java.util.concurrent.TimeUnit
class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback {

private val viewModel: RoomDirectoryViewModel by activityViewModel()
private val navigationViewModel: RoomDirectoryNavigationViewModel by activityViewModel()
private val publicRoomsController: PublicRoomsController by inject()
private val errorFormatter: ErrorFormatter by inject()

@ -76,8 +77,7 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback
.disposeOnDestroy()

publicRoomsCreateNewRoom.setOnClickListener {
// TODO Not the best navigation pattern
(vectorBaseActivity as? RoomDirectoryActivity)?.gotoCreateRoom()
navigationViewModel.goTo(RoomDirectoryActivity.Navigation.CreateRoom)
}

viewModel.joinRoomErrorLiveData.observe(this, Observer {

View File

@ -17,6 +17,8 @@
package im.vector.riotredesign.features.roomdirectory

import android.os.Bundle
import androidx.lifecycle.Observer
import com.airbnb.mvrx.viewModel
import im.vector.riotredesign.R
import im.vector.riotredesign.core.extensions.addFragment
import im.vector.riotredesign.core.extensions.addFragmentToBackstack
@ -27,6 +29,14 @@ import org.koin.android.scope.ext.android.getOrCreateScope

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

@ -34,6 +44,13 @@ class RoomDirectoryActivity : VectorBaseActivity() {
super.onCreate(savedInstanceState)

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() {
@ -42,8 +59,7 @@ class RoomDirectoryActivity : VectorBaseActivity() {
}
}


fun gotoCreateRoom() {
private fun gotoCreateRoom() {
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 androidx.recyclerview.widget.LinearLayoutManager
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.riotredesign.R
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.RoomDirectoryNavigationViewModel
import kotlinx.android.synthetic.main.fragment_create_room.*
import org.koin.android.ext.android.inject
import org.koin.android.scope.ext.android.bindScope
@ -33,6 +36,7 @@ import timber.log.Timber

class CreateRoomFragment : VectorBaseFragment(), CreateRoomController.Listener {

private val navigationViewModel: RoomDirectoryNavigationViewModel by activityViewModel()
private val viewModel: CreateRoomViewModel by fragmentViewModel()
private val createRoomController: CreateRoomController by inject()

@ -49,8 +53,7 @@ class CreateRoomFragment : VectorBaseFragment(), CreateRoomController.Listener {
setupRecyclerView()

createRoomClose.setOnClickListener {
// TODO Not the best way to manage Fragment Backstack...
vectorBaseActivity.onBackPressed()
navigationViewModel.goTo(RoomDirectoryActivity.Navigation.Back)
}
}


View File

@ -40,7 +40,7 @@ class CreateRoomViewModel(initialState: CreateRoomViewState,

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) }

@ -59,6 +59,7 @@ class CreateRoomViewModel(initialState: CreateRoomViewState,
// Directory visibility
visibility = if (state.isInRoomDirectory) RoomDirectoryVisibility.PUBLIC else RoomDirectoryVisibility.PRIVATE

// Public room
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.riotredesign.R
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.RoomDirectoryNavigationViewModel
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel
import kotlinx.android.synthetic.main.fragment_room_directory_picker.*
import org.koin.android.ext.android.inject
@ -39,6 +41,7 @@ import timber.log.Timber
class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerController.Callback {

private val viewModel: RoomDirectoryViewModel by activityViewModel()
private val navigationViewModel: RoomDirectoryNavigationViewModel by activityViewModel()
private val pickerViewModel: RoomDirectoryPickerViewModel by fragmentViewModel()
private val roomDirectoryPickerController: RoomDirectoryPickerController by inject()

@ -88,8 +91,7 @@ class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerCon
Timber.v("onRoomDirectoryClicked: $roomDirectoryData")
viewModel.setRoomDirectoryData(roomDirectoryData)

// TODO Not the best way to manage Fragment Backstack...
vectorBaseActivity.onBackPressed()
navigationViewModel.goTo(RoomDirectoryActivity.Navigation.Back)
}

override fun retry() {