Skip to content

Commit 4fedafc

Browse files
authored
Merge pull request #6749 from vector-im/feature/eric/space-list-modal
Adds Space List Bottom Sheet
2 parents 74d99b1 + 8b0fba2 commit 4fedafc

26 files changed

+890
-159
lines changed

changelog.d/6749.wip

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Adds space list bottom sheet for new app layout

vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import im.vector.app.features.home.HomeSharedActionViewModel
3434
import im.vector.app.features.home.room.detail.RoomDetailSharedActionViewModel
3535
import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel
3636
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
37+
import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel
3738
import im.vector.app.features.reactions.EmojiChooserViewModel
3839
import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel
3940
import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel
@@ -157,4 +158,9 @@ interface ViewModelModule {
157158
@IntoMap
158159
@ViewModelKey(SpacePeopleSharedActionViewModel::class)
159160
fun bindSpacePeopleSharedActionViewModel(viewModel: SpacePeopleSharedActionViewModel): ViewModel
161+
162+
@Binds
163+
@IntoMap
164+
@ViewModelKey(RoomListSharedActionViewModel::class)
165+
fun bindRoomListSharedActionViewModel(viewModel: RoomListSharedActionViewModel): ViewModel
160166
}

vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import im.vector.app.features.themes.ThemeUtils
3737
@EpoxyModelClass
3838
abstract class HomeSpaceSummaryItem : VectorEpoxyModel<HomeSpaceSummaryItem.Holder>(R.layout.item_space) {
3939

40+
@EpoxyAttribute var text: String = ""
4041
@EpoxyAttribute var selected: Boolean = false
4142
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
4243
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2019 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package im.vector.app.features.grouplist
19+
20+
import android.content.res.ColorStateList
21+
import android.widget.ImageView
22+
import android.widget.TextView
23+
import androidx.core.content.ContextCompat
24+
import androidx.core.graphics.ColorUtils
25+
import com.airbnb.epoxy.EpoxyAttribute
26+
import com.airbnb.epoxy.EpoxyModelClass
27+
import im.vector.app.R
28+
import im.vector.app.core.epoxy.ClickListener
29+
import im.vector.app.core.epoxy.VectorEpoxyHolder
30+
import im.vector.app.core.epoxy.VectorEpoxyModel
31+
import im.vector.app.core.epoxy.onClick
32+
import im.vector.app.core.platform.CheckableConstraintLayout
33+
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
34+
import im.vector.app.features.themes.ThemeUtils
35+
36+
@EpoxyModelClass
37+
abstract class NewHomeSpaceSummaryItem : VectorEpoxyModel<NewHomeSpaceSummaryItem.Holder>(R.layout.item_new_space) {
38+
39+
@EpoxyAttribute var text: String = ""
40+
@EpoxyAttribute var selected: Boolean = false
41+
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
42+
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false)
43+
@EpoxyAttribute var showSeparator: Boolean = false
44+
45+
override fun getViewType() = R.id.space_item_home
46+
47+
override fun bind(holder: Holder) {
48+
super.bind(holder)
49+
holder.root.onClick(listener)
50+
holder.name.text = holder.view.context.getString(R.string.all_chats)
51+
holder.root.isChecked = selected
52+
holder.root.context.resources
53+
holder.avatar.background = ContextCompat.getDrawable(holder.view.context, R.drawable.new_space_home_background)
54+
holder.avatar.backgroundTintList = ColorStateList.valueOf(
55+
ColorUtils.setAlphaComponent(ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_tertiary), (255 * 0.3).toInt()))
56+
holder.avatar.setImageResource(R.drawable.ic_space_home)
57+
holder.avatar.imageTintList = ColorStateList.valueOf(ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_primary))
58+
holder.avatar.scaleType = ImageView.ScaleType.CENTER_INSIDE
59+
60+
holder.unreadCounter.render(countState)
61+
}
62+
63+
class Holder : VectorEpoxyHolder() {
64+
val root by bind<CheckableConstraintLayout>(R.id.root)
65+
val avatar by bind<ImageView>(R.id.avatar)
66+
val name by bind<TextView>(R.id.name)
67+
val unreadCounter by bind<UnreadCounterBadgeView>(R.id.unread_counter)
68+
}
69+
}

vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt

+6-6
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class NewHomeDetailFragment @Inject constructor(
6767
private val alertManager: PopupAlertManager,
6868
private val callManager: WebRtcCallManager,
6969
private val vectorPreferences: VectorPreferences,
70-
private val appStateHandler: SpaceStateHandler,
70+
private val spaceStateHandler: SpaceStateHandler,
7171
private val session: Session,
7272
) : VectorBaseFragment<FragmentNewHomeDetailBinding>(),
7373
KeysBackupBanner.Delegate,
@@ -176,13 +176,13 @@ class NewHomeDetailFragment @Inject constructor(
176176
}
177177

178178
private fun navigateBack() {
179-
val previousSpaceId = appStateHandler.getSpaceBackstack().removeLastOrNull()
180-
val parentSpaceId = appStateHandler.getCurrentSpace()?.flattenParentIds?.lastOrNull()
179+
val previousSpaceId = spaceStateHandler.getSpaceBackstack().removeLastOrNull()
180+
val parentSpaceId = spaceStateHandler.getCurrentSpace()?.flattenParentIds?.lastOrNull()
181181
setCurrentSpace(previousSpaceId ?: parentSpaceId)
182182
}
183183

184184
private fun setCurrentSpace(spaceId: String?) {
185-
appStateHandler.setCurrentSpace(spaceId, isForwardNavigation = false)
185+
spaceStateHandler.setCurrentSpace(spaceId, isForwardNavigation = false)
186186
sharedActionViewModel.post(HomeActivitySharedAction.OnCloseSpace)
187187
}
188188

@@ -205,7 +205,7 @@ class NewHomeDetailFragment @Inject constructor(
205205
}
206206

207207
private fun refreshSpaceState() {
208-
appStateHandler.getCurrentSpace()?.let {
208+
spaceStateHandler.getCurrentSpace()?.let {
209209
onSpaceChange(it)
210210
}
211211
}
@@ -450,7 +450,7 @@ class NewHomeDetailFragment @Inject constructor(
450450
return this
451451
}
452452

453-
override fun onBackPressed(toolbarButton: Boolean) = if (appStateHandler.getCurrentSpace() != null) {
453+
override fun onBackPressed(toolbarButton: Boolean) = if (spaceStateHandler.getCurrentSpace() != null) {
454454
navigateBack()
455455
true
456456
} else {

vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ sealed class RoomListQuickActionsSharedAction(
2525
@StringRes val titleRes: Int,
2626
@DrawableRes val iconResId: Int?,
2727
val destructive: Boolean = false
28-
) :
29-
VectorSharedAction {
28+
) : VectorSharedAction {
3029

3130
data class NotificationsAllNoisy(val roomId: String) : RoomListQuickActionsSharedAction(
3231
R.string.room_list_quick_actions_notifications_all_noisy,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) 2022 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package im.vector.app.features.home.room.list.actions
18+
19+
import im.vector.app.core.platform.VectorSharedAction
20+
21+
sealed class RoomListSharedAction : VectorSharedAction {
22+
23+
object CloseBottomSheet : RoomListSharedAction()
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright (c) 2022 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package im.vector.app.features.home.room.list.actions
18+
19+
import im.vector.app.core.platform.VectorSharedActionViewModel
20+
import javax.inject.Inject
21+
22+
class RoomListSharedActionViewModel @Inject constructor() : VectorSharedActionViewModel<RoomListSharedAction>()

vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt

+59-39
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,12 @@ import im.vector.app.features.home.room.list.RoomSummaryItemFactory
4343
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
4444
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction
4545
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
46+
import im.vector.app.features.home.room.list.actions.RoomListSharedAction
47+
import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel
4648
import im.vector.app.features.home.room.list.home.filter.HomeFilteredRoomsController
4749
import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
4850
import im.vector.app.features.home.room.list.home.recent.RecentRoomCarouselController
51+
import im.vector.app.features.spaces.SpaceListBottomSheet
4952
import kotlinx.coroutines.flow.launchIn
5053
import kotlinx.coroutines.flow.onEach
5154
import org.matrix.android.sdk.api.session.room.model.RoomSummary
@@ -62,10 +65,13 @@ class HomeRoomListFragment @Inject constructor(
6265
RoomListListener {
6366

6467
private val roomListViewModel: HomeRoomListViewModel by fragmentViewModel()
65-
private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel
68+
private lateinit var sharedQuickActionsViewModel: RoomListQuickActionsSharedActionViewModel
69+
private lateinit var sharedActionViewModel: RoomListSharedActionViewModel
6670
private var concatAdapter = ConcatAdapter()
6771
private var modelBuildListener: OnModelBuildFinishedListener? = null
6872

73+
private val spaceListBottomSheet = SpaceListBottomSheet()
74+
6975
private lateinit var stateRestorer: LayoutManagerStateRestorer
7076

7177
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomListBinding {
@@ -74,15 +80,25 @@ class HomeRoomListFragment @Inject constructor(
7480

7581
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
7682
super.onViewCreated(view, savedInstanceState)
83+
views.stateView.contentView = views.roomListView
84+
views.stateView.state = StateView.State.Loading
85+
setupObservers()
86+
setupRecyclerView()
87+
setupFabs()
88+
}
89+
90+
private fun setupObservers() {
91+
sharedQuickActionsViewModel = activityViewModelProvider[RoomListQuickActionsSharedActionViewModel::class.java]
92+
sharedActionViewModel = activityViewModelProvider[RoomListSharedActionViewModel::class.java]
7793

78-
sharedActionViewModel = activityViewModelProvider[RoomListQuickActionsSharedActionViewModel::class.java]
7994
sharedActionViewModel
8095
.stream()
81-
.onEach { handleQuickActions(it) }
96+
.onEach(::handleSharedAction)
97+
.launchIn(viewLifecycleOwner.lifecycleScope)
98+
sharedQuickActionsViewModel
99+
.stream()
100+
.onEach(::handleQuickActions)
82101
.launchIn(viewLifecycleOwner.lifecycleScope)
83-
84-
views.stateView.contentView = views.roomListView
85-
views.stateView.state = StateView.State.Loading
86102

87103
roomListViewModel.observeViewEvents {
88104
when (it) {
@@ -92,9 +108,42 @@ class HomeRoomListFragment @Inject constructor(
92108
is HomeRoomListViewEvents.Done -> Unit
93109
}
94110
}
111+
}
95112

96-
setupRecyclerView()
97-
setupFabs()
113+
private fun handleSharedAction(action: RoomListSharedAction) {
114+
when (action) {
115+
RoomListSharedAction.CloseBottomSheet -> spaceListBottomSheet.dismiss()
116+
}
117+
}
118+
119+
private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
120+
when (quickAction) {
121+
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
122+
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY))
123+
}
124+
is RoomListQuickActionsSharedAction.NotificationsAll -> {
125+
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES))
126+
}
127+
is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> {
128+
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY))
129+
}
130+
is RoomListQuickActionsSharedAction.NotificationsMute -> {
131+
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE))
132+
}
133+
is RoomListQuickActionsSharedAction.Settings -> {
134+
navigator.openRoomProfile(requireActivity(), quickAction.roomId)
135+
}
136+
is RoomListQuickActionsSharedAction.Favorite -> {
137+
roomListViewModel.handle(HomeRoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_FAVOURITE))
138+
}
139+
is RoomListQuickActionsSharedAction.LowPriority -> {
140+
roomListViewModel.handle(HomeRoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_LOW_PRIORITY))
141+
}
142+
is RoomListQuickActionsSharedAction.Leave -> {
143+
roomListViewModel.handle(HomeRoomListAction.LeaveRoom(quickAction.roomId))
144+
promptLeaveRoom(quickAction.roomId)
145+
}
146+
}
98147
}
99148

100149
private fun setupRecyclerView() {
@@ -121,7 +170,8 @@ class HomeRoomListFragment @Inject constructor(
121170
}
122171

123172
views.newLayoutOpenSpacesButton.setOnClickListener {
124-
// Click action for open spaces modal goes here (Issue #6499)
173+
// Click action for open spaces modal goes here
174+
spaceListBottomSheet.show(requireActivity().supportFragmentManager, SpaceListBottomSheet.TAG)
125175
}
126176

127177
// Hide FABs when list is scrolling
@@ -158,36 +208,6 @@ class HomeRoomListFragment @Inject constructor(
158208
}
159209
}
160210

161-
private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
162-
when (quickAction) {
163-
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
164-
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY))
165-
}
166-
is RoomListQuickActionsSharedAction.NotificationsAll -> {
167-
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES))
168-
}
169-
is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> {
170-
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY))
171-
}
172-
is RoomListQuickActionsSharedAction.NotificationsMute -> {
173-
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE))
174-
}
175-
is RoomListQuickActionsSharedAction.Settings -> {
176-
navigator.openRoomProfile(requireActivity(), quickAction.roomId)
177-
}
178-
is RoomListQuickActionsSharedAction.Favorite -> {
179-
roomListViewModel.handle(HomeRoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_FAVOURITE))
180-
}
181-
is RoomListQuickActionsSharedAction.LowPriority -> {
182-
roomListViewModel.handle(HomeRoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_LOW_PRIORITY))
183-
}
184-
is RoomListQuickActionsSharedAction.Leave -> {
185-
roomListViewModel.handle(HomeRoomListAction.LeaveRoom(quickAction.roomId))
186-
promptLeaveRoom(quickAction.roomId)
187-
}
188-
}
189-
}
190-
191211
private fun promptLeaveRoom(roomId: String) {
192212
val isPublicRoom = roomListViewModel.isPublicRoom(roomId)
193213
val message = buildString {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2021 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package im.vector.app.features.spaces
18+
19+
import android.content.res.ColorStateList
20+
import android.widget.ImageView
21+
import com.airbnb.epoxy.EpoxyAttribute
22+
import com.airbnb.epoxy.EpoxyModelClass
23+
import im.vector.app.R
24+
import im.vector.app.core.epoxy.ClickListener
25+
import im.vector.app.core.epoxy.VectorEpoxyHolder
26+
import im.vector.app.core.epoxy.VectorEpoxyModel
27+
import im.vector.app.core.epoxy.onClick
28+
import im.vector.app.features.themes.ThemeUtils
29+
30+
@EpoxyModelClass
31+
abstract class NewSpaceAddItem : VectorEpoxyModel<NewSpaceAddItem.Holder>(R.layout.item_new_space_add) {
32+
33+
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
34+
35+
override fun bind(holder: Holder) {
36+
super.bind(holder)
37+
holder.view.onClick(listener)
38+
39+
holder.plus.imageTintList = ColorStateList.valueOf(ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_primary))
40+
}
41+
42+
class Holder : VectorEpoxyHolder() {
43+
val plus by bind<ImageView>(R.id.plus)
44+
}
45+
}

0 commit comments

Comments
 (0)