forked from GitHub-Mirror/riotX-android
Room list : quickly branch filter room name field
This commit is contained in:
parent
32b29c47e7
commit
85608b04d1
@ -0,0 +1,44 @@
|
|||||||
|
package im.vector.riotredesign.core.extensions
|
||||||
|
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.InputType
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import android.widget.EditText
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
|
||||||
|
fun EditText.setupAsSearch() {
|
||||||
|
addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun afterTextChanged(editable: Editable?) {
|
||||||
|
val clearIcon = if (editable?.isNotEmpty() == true) R.drawable.ic_clear_white else 0
|
||||||
|
setCompoundDrawablesWithIntrinsicBounds(0, 0, clearIcon, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
|
||||||
|
})
|
||||||
|
|
||||||
|
maxLines = 1
|
||||||
|
inputType = InputType.TYPE_CLASS_TEXT
|
||||||
|
imeOptions = EditorInfo.IME_ACTION_SEARCH
|
||||||
|
setOnEditorActionListener { _, actionId, event ->
|
||||||
|
var consumed = false
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||||
|
hideKeyboard()
|
||||||
|
consumed = true
|
||||||
|
}
|
||||||
|
consumed
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnTouchListener(View.OnTouchListener { _, event ->
|
||||||
|
if (event.action == MotionEvent.ACTION_UP) {
|
||||||
|
if (event.rawX >= (this.right - this.compoundPaddingRight)) {
|
||||||
|
text = null
|
||||||
|
return@OnTouchListener true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@OnTouchListener false
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package im.vector.riotredesign.core.extensions
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
|
||||||
|
fun View.hideKeyboard() {
|
||||||
|
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.hideSoftInputFromWindow(windowToken, 0)
|
||||||
|
}
|
@ -8,4 +8,6 @@ sealed class RoomListActions {
|
|||||||
|
|
||||||
object RoomDisplayed : RoomListActions()
|
object RoomDisplayed : RoomListActions()
|
||||||
|
|
||||||
|
data class FilterRooms(val roomName: CharSequence? = null) : RoomListActions()
|
||||||
|
|
||||||
}
|
}
|
@ -1,9 +1,12 @@
|
|||||||
package im.vector.riotredesign.features.home.room.list
|
package im.vector.riotredesign.features.home.room.list
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.Incomplete
|
import com.airbnb.mvrx.Incomplete
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
@ -11,6 +14,8 @@ import com.airbnb.mvrx.activityViewModel
|
|||||||
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.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.extensions.hideKeyboard
|
||||||
|
import im.vector.riotredesign.core.extensions.setupAsSearch
|
||||||
import im.vector.riotredesign.core.platform.RiotFragment
|
import im.vector.riotredesign.core.platform.RiotFragment
|
||||||
import im.vector.riotredesign.core.platform.StateView
|
import im.vector.riotredesign.core.platform.StateView
|
||||||
import im.vector.riotredesign.features.home.HomeNavigator
|
import im.vector.riotredesign.features.home.HomeNavigator
|
||||||
@ -38,6 +43,7 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
|
|||||||
roomController = RoomSummaryController(this)
|
roomController = RoomSummaryController(this)
|
||||||
stateView.contentView = epoxyRecyclerView
|
stateView.contentView = epoxyRecyclerView
|
||||||
epoxyRecyclerView.setController(roomController)
|
epoxyRecyclerView.setController(roomController)
|
||||||
|
setupFilterView()
|
||||||
homeViewModel.subscribe { renderState(it) }
|
homeViewModel.subscribe { renderState(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +76,21 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
|
|||||||
stateView.state = StateView.State.Error(message)
|
stateView.state = StateView.State.Error(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setupFilterView() {
|
||||||
|
filterRoomView.setupAsSearch()
|
||||||
|
filterRoomView.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun afterTextChanged(s: Editable?) = Unit
|
||||||
|
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
||||||
|
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
|
homeViewModel.accept(RoomListActions.FilterRooms(s))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoomSummaryController.Callback **************************************************************
|
||||||
|
|
||||||
override fun onRoomSelected(room: RoomSummary) {
|
override fun onRoomSelected(room: RoomSummary) {
|
||||||
homeViewModel.accept(RoomListActions.SelectRoom(room))
|
homeViewModel.accept(RoomListActions.SelectRoom(room))
|
||||||
homeNavigator.openRoomDetail(room.roomId, null)
|
homeNavigator.openRoomDetail(room.roomId, null)
|
||||||
|
@ -3,6 +3,7 @@ package im.vector.riotredesign.features.home.room.list
|
|||||||
import arrow.core.Option
|
import arrow.core.Option
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
|
import com.jakewharton.rxrelay2.BehaviorRelay
|
||||||
import im.vector.matrix.android.api.Matrix
|
import im.vector.matrix.android.api.Matrix
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||||
@ -12,10 +13,12 @@ import im.vector.riotredesign.core.platform.RiotViewModel
|
|||||||
import im.vector.riotredesign.features.home.group.SelectedGroupHolder
|
import im.vector.riotredesign.features.home.group.SelectedGroupHolder
|
||||||
import im.vector.riotredesign.features.home.room.VisibleRoomHolder
|
import im.vector.riotredesign.features.home.room.VisibleRoomHolder
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.functions.BiFunction
|
import io.reactivex.functions.Function3
|
||||||
import io.reactivex.rxkotlin.subscribeBy
|
import io.reactivex.rxkotlin.subscribeBy
|
||||||
import org.koin.android.ext.android.get
|
import org.koin.android.ext.android.get
|
||||||
|
|
||||||
|
typealias RoomListFilterName = CharSequence
|
||||||
|
|
||||||
class RoomListViewModel(initialState: RoomListViewState,
|
class RoomListViewModel(initialState: RoomListViewState,
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val selectedGroupHolder: SelectedGroupHolder,
|
private val selectedGroupHolder: SelectedGroupHolder,
|
||||||
@ -35,6 +38,9 @@ class RoomListViewModel(initialState: RoomListViewState,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val roomListFilter = BehaviorRelay.createDefault<Option<RoomListFilterName>>(Option.empty())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
observeRoomSummaries()
|
observeRoomSummaries()
|
||||||
observeVisibleRoom()
|
observeVisibleRoom()
|
||||||
@ -43,6 +49,7 @@ class RoomListViewModel(initialState: RoomListViewState,
|
|||||||
fun accept(action: RoomListActions) {
|
fun accept(action: RoomListActions) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RoomListActions.SelectRoom -> handleSelectRoom(action)
|
is RoomListActions.SelectRoom -> handleSelectRoom(action)
|
||||||
|
is RoomListActions.FilterRooms -> handleFilterRooms(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +61,11 @@ class RoomListViewModel(initialState: RoomListViewState,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleFilterRooms(action: RoomListActions.FilterRooms) {
|
||||||
|
val optionalFilter = Option.fromNullable(action.roomName)
|
||||||
|
roomListFilter.accept(optionalFilter)
|
||||||
|
}
|
||||||
|
|
||||||
private fun observeVisibleRoom() {
|
private fun observeVisibleRoom() {
|
||||||
visibleRoomHolder.visibleRoom()
|
visibleRoomHolder.visibleRoom()
|
||||||
.subscribeBy {
|
.subscribeBy {
|
||||||
@ -63,13 +75,22 @@ class RoomListViewModel(initialState: RoomListViewState,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun observeRoomSummaries() {
|
private fun observeRoomSummaries() {
|
||||||
Observable.combineLatest<List<RoomSummary>, Option<GroupSummary>, RoomSummaries>(
|
Observable.combineLatest<List<RoomSummary>, Option<GroupSummary>, Option<RoomListFilterName>, RoomSummaries>(
|
||||||
session.rx().liveRoomSummaries(),
|
session.rx().liveRoomSummaries(),
|
||||||
selectedGroupHolder.selectedGroup(),
|
selectedGroupHolder.selectedGroup(),
|
||||||
BiFunction { rooms, selectedGroupOption ->
|
roomListFilter,
|
||||||
val selectedGroup = selectedGroupOption.orNull()
|
Function3 { rooms, selectedGroupOption, filterRoomOption ->
|
||||||
|
val filterRoom = filterRoomOption.orNull()
|
||||||
|
val filteredRooms = rooms.filter {
|
||||||
|
if (filterRoom.isNullOrBlank()) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
it.displayName.contains(other = filterRoom, ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val filteredDirectRooms = rooms
|
val selectedGroup = selectedGroupOption.orNull()
|
||||||
|
val filteredDirectRooms = filteredRooms
|
||||||
.filter { it.isDirect }
|
.filter { it.isDirect }
|
||||||
.filter {
|
.filter {
|
||||||
if (selectedGroup == null) {
|
if (selectedGroup == null) {
|
||||||
@ -81,7 +102,7 @@ class RoomListViewModel(initialState: RoomListViewState,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val filteredGroupRooms = rooms
|
val filteredGroupRooms = filteredRooms
|
||||||
.filter { !it.isDirect }
|
.filter { !it.isDirect }
|
||||||
.filter {
|
.filter {
|
||||||
selectedGroup?.roomIds?.contains(it.roomId) ?: true
|
selectedGroup?.roomIds?.contains(it.roomId) ?: true
|
||||||
|
BIN
app/src/main/res/drawable-hdpi/ic_clear_white.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_clear_white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 276 B |
BIN
app/src/main/res/drawable-mdpi/ic_clear_white.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_clear_white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 209 B |
BIN
app/src/main/res/drawable-xhdpi/ic_clear_white.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_clear_white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 329 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_clear_white.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_clear_white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 462 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_clear_white.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_clear_white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 601 B |
@ -11,15 +11,16 @@
|
|||||||
android:id="@+id/filterRoomView"
|
android:id="@+id/filterRoomView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="32dp"
|
android:layout_height="32dp"
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
android:background="@drawable/bg_search_edit_text"
|
android:background="@drawable/bg_search_edit_text"
|
||||||
android:drawableLeft="@drawable/ic_search_white"
|
android:drawableLeft="@drawable/ic_search_white"
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
android:drawableTint="#9fa9ba"
|
android:drawableTint="#9fa9ba"
|
||||||
android:hint="Filter by name..."
|
android:hint="Filter by name..."
|
||||||
|
android:lines="1"
|
||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/stateView"
|
app:layout_constraintBottom_toTopOf="@+id/stateView"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
@ -30,8 +31,8 @@
|
|||||||
android:id="@+id/stateView"
|
android:id="@+id/stateView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_marginBottom="0dp"
|
|
||||||
android:layout_marginEnd="0dp"
|
android:layout_marginEnd="0dp"
|
||||||
|
android:layout_marginBottom="0dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
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"
|
||||||
|
Loading…
Reference in New Issue
Block a user