mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-23 18:05:59 +03:00
Filter rooms
This commit is contained in:
parent
fc1c0caea3
commit
3c25088243
21 changed files with 390 additions and 43 deletions
|
@ -60,6 +60,7 @@
|
||||||
android:label="@string/title_activity_emoji_reaction_picker" />
|
android:label="@string/title_activity_emoji_reaction_picker" />
|
||||||
<activity android:name=".features.roomdirectory.RoomDirectoryActivity" />
|
<activity android:name=".features.roomdirectory.RoomDirectoryActivity" />
|
||||||
<activity android:name=".features.roomdirectory.roompreview.RoomPreviewActivity" />
|
<activity android:name=".features.roomdirectory.roompreview.RoomPreviewActivity" />
|
||||||
|
<activity android:name=".features.home.room.filtered.FilteredRoomsActivity" />
|
||||||
<activity android:name=".features.home.room.detail.RoomDetailActivity" />
|
<activity android:name=".features.home.room.detail.RoomDetailActivity" />
|
||||||
<activity android:name=".features.debug.DebugMenuActivity" />
|
<activity android:name=".features.debug.DebugMenuActivity" />
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,14 @@ import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupStep1Frag
|
||||||
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupStep2Fragment
|
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupStep2Fragment
|
||||||
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupStep3Fragment
|
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupStep3Fragment
|
||||||
import im.vector.riotx.features.crypto.verification.SASVerificationIncomingFragment
|
import im.vector.riotx.features.crypto.verification.SASVerificationIncomingFragment
|
||||||
import im.vector.riotx.features.home.*
|
import im.vector.riotx.features.home.HomeActivity
|
||||||
|
import im.vector.riotx.features.home.HomeDetailFragment
|
||||||
|
import im.vector.riotx.features.home.HomeDrawerFragment
|
||||||
|
import im.vector.riotx.features.home.HomeModule
|
||||||
import im.vector.riotx.features.home.group.GroupListFragment
|
import im.vector.riotx.features.home.group.GroupListFragment
|
||||||
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.detail.timeline.action.*
|
import im.vector.riotx.features.home.room.detail.timeline.action.*
|
||||||
|
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
|
||||||
import im.vector.riotx.features.home.room.list.RoomListFragment
|
import im.vector.riotx.features.home.room.list.RoomListFragment
|
||||||
import im.vector.riotx.features.invite.VectorInviteView
|
import im.vector.riotx.features.invite.VectorInviteView
|
||||||
import im.vector.riotx.features.login.LoginActivity
|
import im.vector.riotx.features.login.LoginActivity
|
||||||
|
@ -130,6 +134,8 @@ interface ScreenComponent {
|
||||||
|
|
||||||
fun inject(imageMediaViewerActivity: ImageMediaViewerActivity)
|
fun inject(imageMediaViewerActivity: ImageMediaViewerActivity)
|
||||||
|
|
||||||
|
fun inject(filteredRoomsActivity: FilteredRoomsActivity)
|
||||||
|
|
||||||
fun inject(vectorInviteView: VectorInviteView)
|
fun inject(vectorInviteView: VectorInviteView)
|
||||||
|
|
||||||
fun inject(videoMediaViewerActivity: VideoMediaViewerActivity)
|
fun inject(videoMediaViewerActivity: VideoMediaViewerActivity)
|
||||||
|
|
|
@ -23,8 +23,8 @@ fun AppCompatActivity.addFragment(fragment: Fragment, frameId: Int) {
|
||||||
supportFragmentManager.inTransaction { add(frameId, fragment) }
|
supportFragmentManager.inTransaction { add(frameId, fragment) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun AppCompatActivity.replaceFragment(fragment: Fragment, frameId: Int) {
|
fun AppCompatActivity.replaceFragment(fragment: Fragment, frameId: Int, tag: String? = null) {
|
||||||
supportFragmentManager.inTransaction { replace(frameId, fragment) }
|
supportFragmentManager.inTransaction { replace(frameId, fragment, tag) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun AppCompatActivity.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
|
fun AppCompatActivity.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ 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.core.pushers.PushersManager
|
import im.vector.riotx.core.pushers.PushersManager
|
||||||
import im.vector.riotx.features.disclaimer.showDisclaimerDialog
|
import im.vector.riotx.features.disclaimer.showDisclaimerDialog
|
||||||
|
import im.vector.riotx.features.navigation.Navigator
|
||||||
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
||||||
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
|
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
|
||||||
import im.vector.riotx.features.workers.signout.SignOutViewModel
|
import im.vector.riotx.features.workers.signout.SignOutViewModel
|
||||||
|
@ -64,6 +65,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||||
@Inject lateinit var homeActivityViewModelFactory: HomeActivityViewModel.Factory
|
@Inject lateinit var homeActivityViewModelFactory: HomeActivityViewModel.Factory
|
||||||
@Inject lateinit var homeNavigator: HomeNavigator
|
@Inject lateinit var homeNavigator: HomeNavigator
|
||||||
|
@Inject lateinit var navigator: Navigator
|
||||||
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
|
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
|
||||||
@Inject lateinit var pushManager: PushersManager
|
@Inject lateinit var pushManager: PushersManager
|
||||||
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
|
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
|
||||||
|
@ -192,6 +194,10 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
bugReporter.openBugReportScreen(this, false)
|
bugReporter.openBugReportScreen(this, false)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
R.id.menu_home_filter -> {
|
||||||
|
navigator.openRoomsFiltering(this)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -35,7 +35,7 @@ class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
val roomDetailArgs: RoomDetailArgs = intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
|
val roomDetailArgs: RoomDetailArgs = intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
|
||||||
?: return
|
?: return
|
||||||
val roomDetailFragment = RoomDetailFragment.newInstance(roomDetailArgs)
|
val roomDetailFragment = RoomDetailFragment.newInstance(roomDetailArgs)
|
||||||
replaceFragment(roomDetailFragment, R.id.roomDetailContainer)
|
replaceFragment(roomDetailFragment, R.id.roomDetailContainer)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* 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.home.room.filtered
|
||||||
|
|
||||||
|
import android.widget.Button
|
||||||
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||||
|
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||||
|
import im.vector.riotx.features.home.room.list.widget.FabMenuView
|
||||||
|
|
||||||
|
@EpoxyModelClass(layout = R.layout.item_room_filter_footer)
|
||||||
|
abstract class FilteredRoomFooterItem : VectorEpoxyModel<FilteredRoomFooterItem.Holder>() {
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var listener: FilteredRoomFooterItemListener? = null
|
||||||
|
|
||||||
|
override fun bind(holder: Holder) {
|
||||||
|
holder.createRoomButton.setOnClickListener { listener?.createRoom() }
|
||||||
|
holder.createDirectChat.setOnClickListener { listener?.createDirectChat() }
|
||||||
|
holder.openRoomDirectory.setOnClickListener { listener?.openRoomDirectory() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
val createRoomButton by bind<Button>(R.id.roomFilterFooterCreateRoom)
|
||||||
|
val createDirectChat by bind<Button>(R.id.roomFilterFooterCreateDirect)
|
||||||
|
val openRoomDirectory by bind<Button>(R.id.roomFilterFooterOpenRoomDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FilteredRoomFooterItemListener : FabMenuView.Listener {
|
||||||
|
fun createRoom()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* 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.home.room.filtered
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.widget.SearchView
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
|
import im.vector.riotx.core.extensions.replaceFragment
|
||||||
|
import im.vector.riotx.core.platform.ToolbarConfigurable
|
||||||
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
|
import im.vector.riotx.features.home.room.list.RoomListFragment
|
||||||
|
import im.vector.riotx.features.home.room.list.RoomListParams
|
||||||
|
import kotlinx.android.synthetic.main.activity_filtered_rooms.*
|
||||||
|
|
||||||
|
class FilteredRoomsActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
|
|
||||||
|
private lateinit var roomListFragment: RoomListFragment
|
||||||
|
|
||||||
|
override fun getLayoutRes(): Int {
|
||||||
|
return R.layout.activity_filtered_rooms
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
|
injector.inject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
if (isFirstCreation()) {
|
||||||
|
roomListFragment = RoomListFragment.newInstance(RoomListParams(RoomListFragment.DisplayMode.FILTERED))
|
||||||
|
replaceFragment(roomListFragment, R.id.filteredRoomsFragmentContainer, FRAGMENT_TAG)
|
||||||
|
} else {
|
||||||
|
roomListFragment = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as RoomListFragment
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredRoomsSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
|
override fun onQueryTextSubmit(query: String): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueryTextChange(newText: String): Boolean {
|
||||||
|
// TODO Create a viewModel and remove this public fun
|
||||||
|
roomListFragment.filterRoomsWith(newText)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Open the keyboard immediately
|
||||||
|
filteredRoomsSearchView.requestFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun configure(toolbar: Toolbar) {
|
||||||
|
configureToolbar(toolbar)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val FRAGMENT_TAG = "RoomListFragment"
|
||||||
|
|
||||||
|
fun newIntent(context: Context): Intent {
|
||||||
|
return Intent(context, FilteredRoomsActivity::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,4 +28,6 @@ sealed class RoomListActions {
|
||||||
|
|
||||||
data class RejectInvitation(val roomSummary: RoomSummary) : RoomListActions()
|
data class RejectInvitation(val roomSummary: RoomSummary) : RoomListActions()
|
||||||
|
|
||||||
|
data class FilterWith(val filter: String) : RoomListActions()
|
||||||
|
|
||||||
}
|
}
|
|
@ -27,9 +27,10 @@ class RoomListDisplayModeFilter(private val displayMode: RoomListFragment.Displa
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return when (displayMode) {
|
return when (displayMode) {
|
||||||
RoomListFragment.DisplayMode.HOME -> roomSummary.notificationCount > 0 || roomSummary.membership == Membership.INVITE
|
RoomListFragment.DisplayMode.HOME -> roomSummary.notificationCount > 0 || roomSummary.membership == Membership.INVITE
|
||||||
RoomListFragment.DisplayMode.PEOPLE -> roomSummary.isDirect && roomSummary.membership == Membership.JOIN
|
RoomListFragment.DisplayMode.PEOPLE -> roomSummary.isDirect && roomSummary.membership == Membership.JOIN
|
||||||
RoomListFragment.DisplayMode.ROOMS -> !roomSummary.isDirect && roomSummary.membership == Membership.JOIN
|
RoomListFragment.DisplayMode.ROOMS -> !roomSummary.isDirect && roomSummary.membership == Membership.JOIN
|
||||||
|
RoomListFragment.DisplayMode.FILTERED -> roomSummary.membership == Membership.JOIN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -53,7 +53,8 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
||||||
enum class DisplayMode(@StringRes val titleRes: Int) {
|
enum class DisplayMode(@StringRes val titleRes: Int) {
|
||||||
HOME(R.string.bottom_action_home),
|
HOME(R.string.bottom_action_home),
|
||||||
PEOPLE(R.string.bottom_action_people_x),
|
PEOPLE(R.string.bottom_action_people_x),
|
||||||
ROOMS(R.string.bottom_action_rooms)
|
ROOMS(R.string.bottom_action_rooms),
|
||||||
|
FILTERED(/* Not used */ R.string.bottom_action_rooms)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -97,9 +98,10 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
||||||
|
|
||||||
private fun setupCreateRoomButton() {
|
private fun setupCreateRoomButton() {
|
||||||
when (roomListParams.displayMode) {
|
when (roomListParams.displayMode) {
|
||||||
DisplayMode.HOME -> createChatFabMenu.isVisible = true
|
DisplayMode.HOME -> createChatFabMenu.isVisible = true
|
||||||
DisplayMode.PEOPLE -> createChatRoomButton.isVisible = true
|
DisplayMode.PEOPLE -> createChatRoomButton.isVisible = true
|
||||||
else -> createGroupRoomButton.isVisible = true
|
DisplayMode.ROOMS -> createGroupRoomButton.isVisible = true
|
||||||
|
DisplayMode.FILTERED -> Unit // No button in this mode
|
||||||
}
|
}
|
||||||
|
|
||||||
createChatRoomButton.setOnClickListener {
|
createChatRoomButton.setOnClickListener {
|
||||||
|
@ -122,9 +124,10 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
||||||
RecyclerView.SCROLL_STATE_DRAGGING,
|
RecyclerView.SCROLL_STATE_DRAGGING,
|
||||||
RecyclerView.SCROLL_STATE_SETTLING -> {
|
RecyclerView.SCROLL_STATE_SETTLING -> {
|
||||||
when (roomListParams.displayMode) {
|
when (roomListParams.displayMode) {
|
||||||
DisplayMode.HOME -> createChatFabMenu.hide()
|
DisplayMode.HOME -> createChatFabMenu.hide()
|
||||||
DisplayMode.PEOPLE -> createChatRoomButton.hide()
|
DisplayMode.PEOPLE -> createChatRoomButton.hide()
|
||||||
else -> createGroupRoomButton.hide()
|
DisplayMode.ROOMS -> createGroupRoomButton.hide()
|
||||||
|
DisplayMode.FILTERED -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,6 +135,9 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun filterRoomsWith(filter: String) {
|
||||||
|
roomListViewModel.accept(RoomListActions.FilterWith(filter))
|
||||||
|
}
|
||||||
|
|
||||||
override fun openRoomDirectory() {
|
override fun openRoomDirectory() {
|
||||||
navigator.openRoomDirectory(requireActivity())
|
navigator.openRoomDirectory(requireActivity())
|
||||||
|
@ -155,9 +161,10 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
||||||
private val showFabRunnable = Runnable {
|
private val showFabRunnable = Runnable {
|
||||||
if (isAdded) {
|
if (isAdded) {
|
||||||
when (roomListParams.displayMode) {
|
when (roomListParams.displayMode) {
|
||||||
DisplayMode.HOME -> createChatFabMenu.show()
|
DisplayMode.HOME -> createChatFabMenu.show()
|
||||||
DisplayMode.PEOPLE -> createChatRoomButton.show()
|
DisplayMode.PEOPLE -> createChatRoomButton.show()
|
||||||
else -> createGroupRoomButton.show()
|
DisplayMode.ROOMS -> createGroupRoomButton.show()
|
||||||
|
DisplayMode.FILTERED -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,7 +195,7 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
||||||
}
|
}
|
||||||
.isNullOrEmpty()
|
.isNullOrEmpty()
|
||||||
val emptyState = when (roomListParams.displayMode) {
|
val emptyState = when (roomListParams.displayMode) {
|
||||||
DisplayMode.HOME -> {
|
DisplayMode.HOME -> {
|
||||||
if (hasNoRoom) {
|
if (hasNoRoom) {
|
||||||
StateView.State.Empty(
|
StateView.State.Empty(
|
||||||
getString(R.string.room_list_catchup_welcome_title),
|
getString(R.string.room_list_catchup_welcome_title),
|
||||||
|
@ -202,18 +209,21 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
||||||
getString(R.string.room_list_catchup_empty_body))
|
getString(R.string.room_list_catchup_empty_body))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DisplayMode.PEOPLE ->
|
DisplayMode.PEOPLE ->
|
||||||
StateView.State.Empty(
|
StateView.State.Empty(
|
||||||
getString(R.string.room_list_people_empty_title),
|
getString(R.string.room_list_people_empty_title),
|
||||||
ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_chat),
|
ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_chat),
|
||||||
getString(R.string.room_list_people_empty_body)
|
getString(R.string.room_list_people_empty_body)
|
||||||
)
|
)
|
||||||
DisplayMode.ROOMS ->
|
DisplayMode.ROOMS ->
|
||||||
StateView.State.Empty(
|
StateView.State.Empty(
|
||||||
getString(R.string.room_list_rooms_empty_title),
|
getString(R.string.room_list_rooms_empty_title),
|
||||||
ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_group),
|
ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_group),
|
||||||
getString(R.string.room_list_rooms_empty_body)
|
getString(R.string.room_list_rooms_empty_body)
|
||||||
)
|
)
|
||||||
|
DisplayMode.FILTERED ->
|
||||||
|
// Always display the content in this mode, because if the footer
|
||||||
|
StateView.State.Content
|
||||||
}
|
}
|
||||||
stateView.state = emptyState
|
stateView.state = emptyState
|
||||||
}
|
}
|
||||||
|
@ -255,4 +265,10 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
||||||
override fun onToggleRoomCategory(roomCategory: RoomCategory) {
|
override fun onToggleRoomCategory(roomCategory: RoomCategory) {
|
||||||
roomListViewModel.accept(RoomListActions.ToggleCategory(roomCategory))
|
roomListViewModel.accept(RoomListActions.ToggleCategory(roomCategory))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun createRoom() {
|
||||||
|
// TODO Create an Activity to host CreateRoomFragment
|
||||||
|
navigator.openRoomDirectory(requireActivity())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -14,8 +14,21 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.riotx.features.home.room
|
package im.vector.riotx.features.home.room.list
|
||||||
|
|
||||||
import im.vector.riotx.core.utils.RxStore
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
|
import io.reactivex.functions.Predicate
|
||||||
|
|
||||||
class VisibleRoomStore : RxStore<String>()
|
class RoomListNameFilter : Predicate<RoomSummary> {
|
||||||
|
|
||||||
|
var filter: String = ""
|
||||||
|
|
||||||
|
override fun test(roomSummary: RoomSummary): Boolean {
|
||||||
|
if (filter.isBlank()) {
|
||||||
|
// No filter
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return roomSummary.displayName.contains(filter, ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
|
@ -76,6 +76,7 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room
|
||||||
is RoomListActions.ToggleCategory -> handleToggleCategory(action)
|
is RoomListActions.ToggleCategory -> handleToggleCategory(action)
|
||||||
is RoomListActions.AcceptInvitation -> handleAcceptInvitation(action)
|
is RoomListActions.AcceptInvitation -> handleAcceptInvitation(action)
|
||||||
is RoomListActions.RejectInvitation -> handleRejectInvitation(action)
|
is RoomListActions.RejectInvitation -> handleRejectInvitation(action)
|
||||||
|
is RoomListActions.FilterWith -> handleFilter(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,10 +90,21 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room
|
||||||
this.toggle(action.category)
|
this.toggle(action.category)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleFilter(action: RoomListActions.FilterWith) {
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
roomFilter = action.filter
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun observeRoomSummaries() {
|
private fun observeRoomSummaries() {
|
||||||
homeRoomListObservableSource
|
homeRoomListObservableSource
|
||||||
.observe()
|
.observe()
|
||||||
|
.observeOn(Schedulers.computation())
|
||||||
|
.map {
|
||||||
|
it.sortedWith(chronologicalRoomComparator)
|
||||||
|
}
|
||||||
.execute { asyncRooms ->
|
.execute { asyncRooms ->
|
||||||
copy(asyncRooms = asyncRooms)
|
copy(asyncRooms = asyncRooms)
|
||||||
}
|
}
|
||||||
|
@ -201,9 +213,10 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room
|
||||||
}
|
}
|
||||||
|
|
||||||
val roomComparator = when (displayMode) {
|
val roomComparator = when (displayMode) {
|
||||||
RoomListFragment.DisplayMode.HOME -> chronologicalRoomComparator
|
RoomListFragment.DisplayMode.HOME -> chronologicalRoomComparator
|
||||||
RoomListFragment.DisplayMode.PEOPLE -> chronologicalRoomComparator
|
RoomListFragment.DisplayMode.PEOPLE -> chronologicalRoomComparator
|
||||||
RoomListFragment.DisplayMode.ROOMS -> chronologicalRoomComparator
|
RoomListFragment.DisplayMode.ROOMS -> chronologicalRoomComparator
|
||||||
|
RoomListFragment.DisplayMode.FILTERED -> chronologicalRoomComparator
|
||||||
}
|
}
|
||||||
|
|
||||||
return RoomSummaries().apply {
|
return RoomSummaries().apply {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import im.vector.riotx.R
|
||||||
data class RoomListViewState(
|
data class RoomListViewState(
|
||||||
val displayMode: RoomListFragment.DisplayMode,
|
val displayMode: RoomListFragment.DisplayMode,
|
||||||
val asyncRooms: Async<List<RoomSummary>> = Uninitialized,
|
val asyncRooms: Async<List<RoomSummary>> = Uninitialized,
|
||||||
|
val roomFilter: String = "",
|
||||||
val asyncFilteredRooms: Async<RoomSummaries> = Uninitialized,
|
val asyncFilteredRooms: Async<RoomSummaries> = Uninitialized,
|
||||||
// List of roomIds that the user wants to join
|
// List of roomIds that the user wants to join
|
||||||
val joiningRoomsIds: Set<String> = emptySet(),
|
val joiningRoomsIds: Set<String> = emptySet(),
|
||||||
|
|
|
@ -20,6 +20,8 @@ import androidx.annotation.StringRes
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
|
import im.vector.riotx.features.home.room.filtered.FilteredRoomFooterItem
|
||||||
|
import im.vector.riotx.features.home.room.filtered.filteredRoomFooterItem
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RoomSummaryController @Inject constructor(private val stringProvider: StringProvider,
|
class RoomSummaryController @Inject constructor(private val stringProvider: StringProvider,
|
||||||
|
@ -28,27 +30,56 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
||||||
|
|
||||||
var listener: Listener? = null
|
var listener: Listener? = null
|
||||||
|
|
||||||
|
private val roomListNameFilter = RoomListNameFilter()
|
||||||
|
|
||||||
override fun buildModels(viewState: RoomListViewState) {
|
override fun buildModels(viewState: RoomListViewState) {
|
||||||
val roomSummaries = viewState.asyncFilteredRooms()
|
if (viewState.displayMode == RoomListFragment.DisplayMode.FILTERED) {
|
||||||
roomSummaries?.forEach { (category, summaries) ->
|
buildFilteredRooms(viewState)
|
||||||
if (summaries.isEmpty()) {
|
} else {
|
||||||
return@forEach
|
val roomSummaries = viewState.asyncFilteredRooms()
|
||||||
} else {
|
roomSummaries?.forEach { (category, summaries) ->
|
||||||
val isExpanded = viewState.isCategoryExpanded(category)
|
if (summaries.isEmpty()) {
|
||||||
buildRoomCategory(viewState, summaries, category.titleRes, viewState.isCategoryExpanded(category)) {
|
return@forEach
|
||||||
listener?.onToggleRoomCategory(category)
|
} else {
|
||||||
}
|
val isExpanded = viewState.isCategoryExpanded(category)
|
||||||
if (isExpanded) {
|
buildRoomCategory(viewState, summaries, category.titleRes, viewState.isCategoryExpanded(category)) {
|
||||||
buildRoomModels(summaries,
|
listener?.onToggleRoomCategory(category)
|
||||||
viewState.joiningRoomsIds,
|
}
|
||||||
viewState.joiningErrorRoomsIds,
|
if (isExpanded) {
|
||||||
viewState.rejectingRoomsIds,
|
buildRoomModels(summaries,
|
||||||
viewState.rejectingErrorRoomsIds)
|
viewState.joiningRoomsIds,
|
||||||
|
viewState.joiningErrorRoomsIds,
|
||||||
|
viewState.rejectingRoomsIds,
|
||||||
|
viewState.rejectingErrorRoomsIds)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun buildFilteredRooms(viewState: RoomListViewState) {
|
||||||
|
val summaries = viewState.asyncRooms() ?: return
|
||||||
|
|
||||||
|
roomListNameFilter.filter = viewState.roomFilter
|
||||||
|
|
||||||
|
val filteredSummaries = summaries.filter { roomListNameFilter.test(it) }
|
||||||
|
|
||||||
|
buildRoomModels(filteredSummaries,
|
||||||
|
viewState.joiningRoomsIds,
|
||||||
|
viewState.joiningErrorRoomsIds,
|
||||||
|
viewState.rejectingRoomsIds,
|
||||||
|
viewState.rejectingErrorRoomsIds)
|
||||||
|
|
||||||
|
addFilterFooter()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addFilterFooter() {
|
||||||
|
filteredRoomFooterItem {
|
||||||
|
id("filter_footer")
|
||||||
|
listener(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildRoomCategory(viewState: RoomListViewState,
|
private fun buildRoomCategory(viewState: RoomListViewState,
|
||||||
summaries: List<RoomSummary>,
|
summaries: List<RoomSummary>,
|
||||||
@StringRes titleRes: Int,
|
@StringRes titleRes: Int,
|
||||||
|
@ -89,7 +120,7 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Listener {
|
interface Listener : FilteredRoomFooterItem.FilteredRoomFooterItemListener {
|
||||||
fun onToggleRoomCategory(roomCategory: RoomCategory)
|
fun onToggleRoomCategory(roomCategory: RoomCategory)
|
||||||
fun onRoomSelected(room: RoomSummary)
|
fun onRoomSelected(room: RoomSummary)
|
||||||
fun onRejectRoomInvitation(room: RoomSummary)
|
fun onRejectRoomInvitation(room: RoomSummary)
|
||||||
|
|
|
@ -27,6 +27,7 @@ import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupActivity
|
||||||
import im.vector.riotx.features.debug.DebugMenuActivity
|
import im.vector.riotx.features.debug.DebugMenuActivity
|
||||||
import im.vector.riotx.features.home.room.detail.RoomDetailActivity
|
import im.vector.riotx.features.home.room.detail.RoomDetailActivity
|
||||||
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
|
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
|
||||||
|
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
|
||||||
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
|
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
|
||||||
import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewActivity
|
import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewActivity
|
||||||
import im.vector.riotx.features.settings.VectorSettingsActivity
|
import im.vector.riotx.features.settings.VectorSettingsActivity
|
||||||
|
@ -61,6 +62,11 @@ class DefaultNavigator @Inject constructor() : Navigator {
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun openRoomsFiltering(context: Context) {
|
||||||
|
val intent = FilteredRoomsActivity.newIntent(context)
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
override fun openSettings(context: Context) {
|
override fun openSettings(context: Context) {
|
||||||
val intent = VectorSettingsActivity.getIntent(context, "TODO")
|
val intent = VectorSettingsActivity.getIntent(context, "TODO")
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
|
|
|
@ -29,6 +29,8 @@ interface Navigator {
|
||||||
|
|
||||||
fun openRoomDirectory(context: Context)
|
fun openRoomDirectory(context: Context)
|
||||||
|
|
||||||
|
fun openRoomsFiltering(context: Context)
|
||||||
|
|
||||||
fun openSettings(context: Context)
|
fun openSettings(context: Context)
|
||||||
|
|
||||||
fun openDebug(context: Context)
|
fun openDebug(context: Context)
|
||||||
|
|
20
vector/src/main/res/drawable/ic_filter.xml
Normal file
20
vector/src/main/res/drawable/ic_filter.xml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M4,6.5h16"
|
||||||
|
android:strokeWidth="1.8"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#03B381"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M6,12.5h12M9,18.5h6"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#03B381"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
42
vector/src/main/res/layout/activity_filtered_rooms.xml
Normal file
42
vector/src/main/res/layout/activity_filtered_rooms.xml
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/filteredRoomsToolbar"
|
||||||
|
style="@style/VectorToolbarStyle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:elevation="4dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.SearchView
|
||||||
|
android:id="@+id/filteredRoomsSearchView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:iconifiedByDefault="false"
|
||||||
|
app:queryHint="@string/home_filter_placeholder_home"
|
||||||
|
app:searchIcon="@drawable/ic_filter" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/filteredRoomsFragmentContainer"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/filteredRoomsToolbar" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
43
vector/src/main/res/layout/item_room_filter_footer.xml
Normal file
43
vector/src/main/res/layout/item_room_filter_footer.xml
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?riotx_background"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="56dp"
|
||||||
|
android:text="@string/room_filtering_footer_title"
|
||||||
|
android:textColor="?riotx_text_secondary"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/roomFilterFooterCreateRoom"
|
||||||
|
style="@style/VectorButtonStyleFlat"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||||
|
android:text="@string/room_filtering_footer_create_new_room" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/roomFilterFooterCreateDirect"
|
||||||
|
style="@style/VectorButtonStyleFlat"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="@string/room_filtering_footer_create_new_direct_message" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/roomFilterFooterOpenRoomDirectory"
|
||||||
|
style="@style/VectorButtonStyleFlat"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginBottom="@dimen/layout_vertical_margin"
|
||||||
|
android:text="@string/room_filtering_footer_open_room_directory" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_home_suggestion"
|
android:id="@+id/menu_home_suggestion"
|
||||||
|
@ -11,4 +12,10 @@
|
||||||
android:icon="@drawable/ic_material_bug_report"
|
android:icon="@drawable/ic_material_bug_report"
|
||||||
android:title="@string/send_bug_report" />
|
android:title="@string/send_bug_report" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_home_filter"
|
||||||
|
android:icon="@drawable/ic_filter"
|
||||||
|
android:title="@string/home_filter_placeholder_home"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
|
@ -25,4 +25,11 @@
|
||||||
<string name="message_edits">Message Edits</string>
|
<string name="message_edits">Message Edits</string>
|
||||||
<string name="no_message_edits_found">No edits found</string>
|
<string name="no_message_edits_found">No edits found</string>
|
||||||
|
|
||||||
|
<!-- Room filtering -->
|
||||||
|
<string name="room_filtering_footer_title">Can’t find what you’re looking for?</string>
|
||||||
|
<string name="room_filtering_footer_create_new_room">Create a new room</string>
|
||||||
|
<string name="room_filtering_footer_create_new_direct_message">Send a new direct message</string>
|
||||||
|
<string name="room_filtering_footer_open_room_directory">View the room directory</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in a new issue