From aa95ce3d022933e80d58a3187336b74aa6a3d520 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 10 Jun 2019 07:57:32 +0200 Subject: [PATCH] Create room screen - Better navigation pattern --- .../core/mvrx/NavigationViewModel.kt | 38 +++++++++++++++++++ .../features/form/FormEditTextItem.kt | 15 ++++---- .../roomdirectory/PublicRoomsFragment.kt | 4 +- .../roomdirectory/RoomDirectoryActivity.kt | 20 +++++++++- .../RoomDirectoryNavigationViewModel.kt | 23 +++++++++++ .../createroom/CreateRoomFragment.kt | 7 +++- .../createroom/CreateRoomViewModel.kt | 3 +- .../picker/RoomDirectoryPickerFragment.kt | 6 ++- 8 files changed, 99 insertions(+), 17 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotredesign/core/mvrx/NavigationViewModel.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryNavigationViewModel.kt diff --git a/vector/src/main/java/im/vector/riotredesign/core/mvrx/NavigationViewModel.kt b/vector/src/main/java/im/vector/riotredesign/core/mvrx/NavigationViewModel.kt new file mode 100644 index 0000000000..70c04f1028 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/core/mvrx/NavigationViewModel.kt @@ -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(initialState: NavigationState) : VectorViewModel(initialState) { + + private val _navigateTo = MutableLiveData>() + val navigateTo: LiveData> + get() = _navigateTo + + + fun goTo(navigation: NavigationClass) { + _navigateTo.postValue(LiveEvent(navigation)) + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/form/FormEditTextItem.kt b/vector/src/main/java/im/vector/riotredesign/features/form/FormEditTextItem.kt index 2d60a9c05d..046d09f715 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/form/FormEditTextItem.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/form/FormEditTextItem.kt @@ -40,6 +40,11 @@ abstract class FormEditTextItem : VectorEpoxyModel() { @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() { } 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() { override fun unbind(holder: Holder) { super.unbind(holder) - - // TODO Remove onTextChanged? - + holder.textInputEditText.removeTextChangedListener(onTextChangeListener) } class Holder : VectorEpoxyHolder() { diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsFragment.kt index 7b8e88fffa..e5524256f4 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsFragment.kt @@ -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 { diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryActivity.kt index 639076213f..c025e82837 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryActivity.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryActivity.kt @@ -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) } } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryNavigationViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryNavigationViewModel.kt new file mode 100644 index 0000000000..d38c9ca198 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryNavigationViewModel.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.features.roomdirectory + +import im.vector.riotredesign.core.mvrx.NavigationState +import im.vector.riotredesign.core.mvrx.NavigationViewModel + +class RoomDirectoryNavigationViewModel(initialState: NavigationState) + : NavigationViewModel(initialState) \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomFragment.kt index 58630118ed..8aa01e9c26 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomFragment.kt @@ -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) } } diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomViewModel.kt index 125330db5d..3790381a5e 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomViewModel.kt @@ -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 } diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt index 51369a91a1..63a62833bb 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt @@ -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() {