mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Merge pull request #6749 from vector-im/feature/eric/space-list-modal
Adds Space List Bottom Sheet
This commit is contained in:
commit
4fedafc1be
26 changed files with 896 additions and 165 deletions
1
changelog.d/6749.wip
Normal file
1
changelog.d/6749.wip
Normal file
|
@ -0,0 +1 @@
|
|||
Adds space list bottom sheet for new app layout
|
|
@ -34,6 +34,7 @@ import im.vector.app.features.home.HomeSharedActionViewModel
|
|||
import im.vector.app.features.home.room.detail.RoomDetailSharedActionViewModel
|
||||
import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel
|
||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
|
||||
import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel
|
||||
import im.vector.app.features.reactions.EmojiChooserViewModel
|
||||
import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel
|
||||
import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel
|
||||
|
@ -157,4 +158,9 @@ interface ViewModelModule {
|
|||
@IntoMap
|
||||
@ViewModelKey(SpacePeopleSharedActionViewModel::class)
|
||||
fun bindSpacePeopleSharedActionViewModel(viewModel: SpacePeopleSharedActionViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(RoomListSharedActionViewModel::class)
|
||||
fun bindRoomListSharedActionViewModel(viewModel: RoomListSharedActionViewModel): ViewModel
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import im.vector.app.features.themes.ThemeUtils
|
|||
@EpoxyModelClass
|
||||
abstract class HomeSpaceSummaryItem : VectorEpoxyModel<HomeSpaceSummaryItem.Holder>(R.layout.item_space) {
|
||||
|
||||
@EpoxyAttribute var text: String = ""
|
||||
@EpoxyAttribute var selected: Boolean = false
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
|
||||
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false)
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.app.features.grouplist
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.ClickListener
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
import im.vector.app.core.platform.CheckableConstraintLayout
|
||||
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
|
||||
@EpoxyModelClass
|
||||
abstract class NewHomeSpaceSummaryItem : VectorEpoxyModel<NewHomeSpaceSummaryItem.Holder>(R.layout.item_new_space) {
|
||||
|
||||
@EpoxyAttribute var text: String = ""
|
||||
@EpoxyAttribute var selected: Boolean = false
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
|
||||
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false)
|
||||
@EpoxyAttribute var showSeparator: Boolean = false
|
||||
|
||||
override fun getViewType() = R.id.space_item_home
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.root.onClick(listener)
|
||||
holder.name.text = holder.view.context.getString(R.string.all_chats)
|
||||
holder.root.isChecked = selected
|
||||
holder.root.context.resources
|
||||
holder.avatar.background = ContextCompat.getDrawable(holder.view.context, R.drawable.new_space_home_background)
|
||||
holder.avatar.backgroundTintList = ColorStateList.valueOf(
|
||||
ColorUtils.setAlphaComponent(ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_tertiary), (255 * 0.3).toInt()))
|
||||
holder.avatar.setImageResource(R.drawable.ic_space_home)
|
||||
holder.avatar.imageTintList = ColorStateList.valueOf(ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_primary))
|
||||
holder.avatar.scaleType = ImageView.ScaleType.CENTER_INSIDE
|
||||
|
||||
holder.unreadCounter.render(countState)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val root by bind<CheckableConstraintLayout>(R.id.root)
|
||||
val avatar by bind<ImageView>(R.id.avatar)
|
||||
val name by bind<TextView>(R.id.name)
|
||||
val unreadCounter by bind<UnreadCounterBadgeView>(R.id.unread_counter)
|
||||
}
|
||||
}
|
|
@ -67,7 +67,7 @@ class NewHomeDetailFragment @Inject constructor(
|
|||
private val alertManager: PopupAlertManager,
|
||||
private val callManager: WebRtcCallManager,
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val appStateHandler: SpaceStateHandler,
|
||||
private val spaceStateHandler: SpaceStateHandler,
|
||||
private val session: Session,
|
||||
) : VectorBaseFragment<FragmentNewHomeDetailBinding>(),
|
||||
KeysBackupBanner.Delegate,
|
||||
|
@ -176,13 +176,13 @@ class NewHomeDetailFragment @Inject constructor(
|
|||
}
|
||||
|
||||
private fun navigateBack() {
|
||||
val previousSpaceId = appStateHandler.getSpaceBackstack().removeLastOrNull()
|
||||
val parentSpaceId = appStateHandler.getCurrentSpace()?.flattenParentIds?.lastOrNull()
|
||||
val previousSpaceId = spaceStateHandler.getSpaceBackstack().removeLastOrNull()
|
||||
val parentSpaceId = spaceStateHandler.getCurrentSpace()?.flattenParentIds?.lastOrNull()
|
||||
setCurrentSpace(previousSpaceId ?: parentSpaceId)
|
||||
}
|
||||
|
||||
private fun setCurrentSpace(spaceId: String?) {
|
||||
appStateHandler.setCurrentSpace(spaceId, isForwardNavigation = false)
|
||||
spaceStateHandler.setCurrentSpace(spaceId, isForwardNavigation = false)
|
||||
sharedActionViewModel.post(HomeActivitySharedAction.OnCloseSpace)
|
||||
}
|
||||
|
||||
|
@ -205,7 +205,7 @@ class NewHomeDetailFragment @Inject constructor(
|
|||
}
|
||||
|
||||
private fun refreshSpaceState() {
|
||||
appStateHandler.getCurrentSpace()?.let {
|
||||
spaceStateHandler.getCurrentSpace()?.let {
|
||||
onSpaceChange(it)
|
||||
}
|
||||
}
|
||||
|
@ -450,7 +450,7 @@ class NewHomeDetailFragment @Inject constructor(
|
|||
return this
|
||||
}
|
||||
|
||||
override fun onBackPressed(toolbarButton: Boolean) = if (appStateHandler.getCurrentSpace() != null) {
|
||||
override fun onBackPressed(toolbarButton: Boolean) = if (spaceStateHandler.getCurrentSpace() != null) {
|
||||
navigateBack()
|
||||
true
|
||||
} else {
|
||||
|
|
|
@ -25,8 +25,7 @@ sealed class RoomListQuickActionsSharedAction(
|
|||
@StringRes val titleRes: Int,
|
||||
@DrawableRes val iconResId: Int?,
|
||||
val destructive: Boolean = false
|
||||
) :
|
||||
VectorSharedAction {
|
||||
) : VectorSharedAction {
|
||||
|
||||
data class NotificationsAllNoisy(val roomId: String) : RoomListQuickActionsSharedAction(
|
||||
R.string.room_list_quick_actions_notifications_all_noisy,
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.app.features.home.room.list.actions
|
||||
|
||||
import im.vector.app.core.platform.VectorSharedAction
|
||||
|
||||
sealed class RoomListSharedAction : VectorSharedAction {
|
||||
|
||||
object CloseBottomSheet : RoomListSharedAction()
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.app.features.home.room.list.actions
|
||||
|
||||
import im.vector.app.core.platform.VectorSharedActionViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomListSharedActionViewModel @Inject constructor() : VectorSharedActionViewModel<RoomListSharedAction>()
|
|
@ -43,9 +43,12 @@ import im.vector.app.features.home.room.list.RoomSummaryItemFactory
|
|||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction
|
||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
|
||||
import im.vector.app.features.home.room.list.actions.RoomListSharedAction
|
||||
import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel
|
||||
import im.vector.app.features.home.room.list.home.filter.HomeFilteredRoomsController
|
||||
import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
|
||||
import im.vector.app.features.home.room.list.home.recent.RecentRoomCarouselController
|
||||
import im.vector.app.features.spaces.SpaceListBottomSheet
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
|
@ -62,10 +65,13 @@ class HomeRoomListFragment @Inject constructor(
|
|||
RoomListListener {
|
||||
|
||||
private val roomListViewModel: HomeRoomListViewModel by fragmentViewModel()
|
||||
private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel
|
||||
private lateinit var sharedQuickActionsViewModel: RoomListQuickActionsSharedActionViewModel
|
||||
private lateinit var sharedActionViewModel: RoomListSharedActionViewModel
|
||||
private var concatAdapter = ConcatAdapter()
|
||||
private var modelBuildListener: OnModelBuildFinishedListener? = null
|
||||
|
||||
private val spaceListBottomSheet = SpaceListBottomSheet()
|
||||
|
||||
private lateinit var stateRestorer: LayoutManagerStateRestorer
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomListBinding {
|
||||
|
@ -74,15 +80,25 @@ class HomeRoomListFragment @Inject constructor(
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
sharedActionViewModel = activityViewModelProvider[RoomListQuickActionsSharedActionViewModel::class.java]
|
||||
sharedActionViewModel
|
||||
.stream()
|
||||
.onEach { handleQuickActions(it) }
|
||||
.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||
|
||||
views.stateView.contentView = views.roomListView
|
||||
views.stateView.state = StateView.State.Loading
|
||||
setupObservers()
|
||||
setupRecyclerView()
|
||||
setupFabs()
|
||||
}
|
||||
|
||||
private fun setupObservers() {
|
||||
sharedQuickActionsViewModel = activityViewModelProvider[RoomListQuickActionsSharedActionViewModel::class.java]
|
||||
sharedActionViewModel = activityViewModelProvider[RoomListSharedActionViewModel::class.java]
|
||||
|
||||
sharedActionViewModel
|
||||
.stream()
|
||||
.onEach(::handleSharedAction)
|
||||
.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||
sharedQuickActionsViewModel
|
||||
.stream()
|
||||
.onEach(::handleQuickActions)
|
||||
.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||
|
||||
roomListViewModel.observeViewEvents {
|
||||
when (it) {
|
||||
|
@ -92,9 +108,42 @@ class HomeRoomListFragment @Inject constructor(
|
|||
is HomeRoomListViewEvents.Done -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupRecyclerView()
|
||||
setupFabs()
|
||||
private fun handleSharedAction(action: RoomListSharedAction) {
|
||||
when (action) {
|
||||
RoomListSharedAction.CloseBottomSheet -> spaceListBottomSheet.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
|
||||
when (quickAction) {
|
||||
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY))
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.NotificationsAll -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES))
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY))
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.NotificationsMute -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE))
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.Settings -> {
|
||||
navigator.openRoomProfile(requireActivity(), quickAction.roomId)
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.Favorite -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_FAVOURITE))
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.LowPriority -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_LOW_PRIORITY))
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.Leave -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.LeaveRoom(quickAction.roomId))
|
||||
promptLeaveRoom(quickAction.roomId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
|
@ -121,7 +170,8 @@ class HomeRoomListFragment @Inject constructor(
|
|||
}
|
||||
|
||||
views.newLayoutOpenSpacesButton.setOnClickListener {
|
||||
// Click action for open spaces modal goes here (Issue #6499)
|
||||
// Click action for open spaces modal goes here
|
||||
spaceListBottomSheet.show(requireActivity().supportFragmentManager, SpaceListBottomSheet.TAG)
|
||||
}
|
||||
|
||||
// Hide FABs when list is scrolling
|
||||
|
@ -158,36 +208,6 @@ class HomeRoomListFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
|
||||
when (quickAction) {
|
||||
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY))
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.NotificationsAll -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES))
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY))
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.NotificationsMute -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE))
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.Settings -> {
|
||||
navigator.openRoomProfile(requireActivity(), quickAction.roomId)
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.Favorite -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_FAVOURITE))
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.LowPriority -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_LOW_PRIORITY))
|
||||
}
|
||||
is RoomListQuickActionsSharedAction.Leave -> {
|
||||
roomListViewModel.handle(HomeRoomListAction.LeaveRoom(quickAction.roomId))
|
||||
promptLeaveRoom(quickAction.roomId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun promptLeaveRoom(roomId: String) {
|
||||
val isPublicRoom = roomListViewModel.isPublicRoom(roomId)
|
||||
val message = buildString {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.app.features.spaces
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.widget.ImageView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.ClickListener
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
|
||||
@EpoxyModelClass
|
||||
abstract class NewSpaceAddItem : VectorEpoxyModel<NewSpaceAddItem.Holder>(R.layout.item_new_space_add) {
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.view.onClick(listener)
|
||||
|
||||
holder.plus.imageTintList = ColorStateList.valueOf(ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_primary))
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val plus by bind<ImageView>(R.id.plus)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.app.features.spaces
|
||||
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
|
||||
@EpoxyModelClass
|
||||
abstract class NewSpaceListHeaderItem : VectorEpoxyModel<NewSpaceListHeaderItem.Holder>(R.layout.item_new_space_list_header) {
|
||||
class Holder : VectorEpoxyHolder()
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.app.features.spaces
|
||||
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.grouplist.newHomeSpaceSummaryItem
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewSpaceSummaryController @Inject constructor(
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
private val stringProvider: StringProvider,
|
||||
) : EpoxyController() {
|
||||
|
||||
var callback: Callback? = null
|
||||
private var viewState: SpaceListViewState? = null
|
||||
|
||||
private val subSpaceComparator: Comparator<SpaceChildInfo> = compareBy<SpaceChildInfo> { it.order }.thenBy { it.childRoomId }
|
||||
|
||||
fun update(viewState: SpaceListViewState) {
|
||||
this.viewState = viewState
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
override fun buildModels() {
|
||||
val nonNullViewState = viewState ?: return
|
||||
buildGroupModels(
|
||||
nonNullViewState.spaces,
|
||||
nonNullViewState.selectedSpace,
|
||||
nonNullViewState.rootSpacesOrdered,
|
||||
nonNullViewState.homeAggregateCount
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildGroupModels(
|
||||
spaceSummaries: List<RoomSummary>?,
|
||||
selectedSpace: RoomSummary?,
|
||||
rootSpaces: List<RoomSummary>?,
|
||||
homeCount: RoomAggregateNotificationCount
|
||||
) {
|
||||
val host = this
|
||||
newSpaceListHeaderItem {
|
||||
id("space_list_header")
|
||||
}
|
||||
|
||||
if (selectedSpace != null) {
|
||||
addSubSpaces(selectedSpace, spaceSummaries, homeCount)
|
||||
} else {
|
||||
addHomeItem(true, homeCount)
|
||||
addRootSpaces(rootSpaces)
|
||||
}
|
||||
|
||||
newSpaceAddItem {
|
||||
id("create")
|
||||
listener { host.callback?.onAddSpaceSelected() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun addHomeItem(selected: Boolean, homeCount: RoomAggregateNotificationCount) {
|
||||
val host = this
|
||||
newHomeSpaceSummaryItem {
|
||||
id("space_home")
|
||||
text(host.stringProvider.getString(R.string.all_chats))
|
||||
selected(selected)
|
||||
countState(UnreadCounterBadgeView.State(homeCount.totalCount, homeCount.isHighlight))
|
||||
listener { host.callback?.onSpaceSelected(null) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun addSubSpaces(
|
||||
selectedSpace: RoomSummary,
|
||||
spaceSummaries: List<RoomSummary>?,
|
||||
homeCount: RoomAggregateNotificationCount,
|
||||
) {
|
||||
val host = this
|
||||
val spaceChildren = selectedSpace.spaceChildren
|
||||
var subSpacesAdded = false
|
||||
|
||||
spaceChildren?.sortedWith(subSpaceComparator)?.forEach { spaceChild ->
|
||||
val subSpaceSummary = spaceSummaries?.firstOrNull { it.roomId == spaceChild.childRoomId } ?: return@forEach
|
||||
|
||||
if (subSpaceSummary.membership != Membership.INVITE) {
|
||||
subSpacesAdded = true
|
||||
newSpaceSummaryItem {
|
||||
avatarRenderer(host.avatarRenderer)
|
||||
id(subSpaceSummary.roomId)
|
||||
matrixItem(subSpaceSummary.toMatrixItem())
|
||||
selected(false)
|
||||
listener { host.callback?.onSpaceSelected(subSpaceSummary) }
|
||||
countState(
|
||||
UnreadCounterBadgeView.State(
|
||||
subSpaceSummary.notificationCount,
|
||||
subSpaceSummary.highlightCount > 0
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!subSpacesAdded) {
|
||||
addHomeItem(false, homeCount)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addRootSpaces(rootSpaces: List<RoomSummary>?) {
|
||||
val host = this
|
||||
rootSpaces
|
||||
?.filter { it.membership != Membership.INVITE }
|
||||
?.forEach { roomSummary ->
|
||||
newSpaceSummaryItem {
|
||||
avatarRenderer(host.avatarRenderer)
|
||||
id(roomSummary.roomId)
|
||||
matrixItem(roomSummary.toMatrixItem())
|
||||
listener { host.callback?.onSpaceSelected(roomSummary) }
|
||||
countState(UnreadCounterBadgeView.State(roomSummary.notificationCount, roomSummary.highlightCount > 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onSpaceSelected(spaceSummary: RoomSummary?)
|
||||
fun onSpaceInviteSelected(spaceSummary: RoomSummary)
|
||||
fun onSpaceSettings(spaceSummary: RoomSummary)
|
||||
fun onAddSpaceSelected()
|
||||
fun sendFeedBack()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.app.features.spaces
|
||||
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.ClickListener
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
import im.vector.app.core.platform.CheckableConstraintLayout
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
||||
@EpoxyModelClass
|
||||
abstract class NewSpaceSummaryItem : VectorEpoxyModel<NewSpaceSummaryItem.Holder>(R.layout.item_new_space) {
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var avatarRenderer: AvatarRenderer
|
||||
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
||||
@EpoxyAttribute var selected: Boolean = false
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
|
||||
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false)
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.rootView.onClick(listener)
|
||||
holder.name.text = matrixItem.displayName
|
||||
holder.rootView.isChecked = selected
|
||||
|
||||
avatarRenderer.render(matrixItem, holder.avatar)
|
||||
holder.unreadCounter.render(countState)
|
||||
}
|
||||
|
||||
override fun unbind(holder: Holder) {
|
||||
avatarRenderer.clear(holder.avatar)
|
||||
super.unbind(holder)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val rootView by bind<CheckableConstraintLayout>(R.id.root)
|
||||
val avatar by bind<ImageView>(R.id.avatar)
|
||||
val name by bind<TextView>(R.id.name)
|
||||
val unreadCounter by bind<UnreadCounterBadgeView>(R.id.unread_counter)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.app.features.spaces
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.replaceChildFragment
|
||||
import im.vector.app.databinding.FragmentSpacesBottomSheetBinding
|
||||
|
||||
class SpaceListBottomSheet : BottomSheetDialogFragment() {
|
||||
|
||||
private lateinit var binding: FragmentSpacesBottomSheetBinding
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
binding = FragmentSpacesBottomSheetBinding.inflate(inflater, container, false)
|
||||
if (savedInstanceState == null) {
|
||||
replaceChildFragment(R.id.space_list, SpaceListFragment::class.java)
|
||||
}
|
||||
return binding.root
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "SpacesBottomSheet"
|
||||
}
|
||||
}
|
|
@ -32,20 +32,29 @@ import im.vector.app.core.extensions.configureWith
|
|||
import im.vector.app.core.platform.StateView
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentSpaceListBinding
|
||||
import im.vector.app.features.VectorFeatures
|
||||
import im.vector.app.features.home.HomeActivitySharedAction
|
||||
import im.vector.app.features.home.HomeSharedActionViewModel
|
||||
import im.vector.app.features.home.room.list.actions.RoomListSharedAction
|
||||
import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* This Fragment is displayed in the navigation drawer [im.vector.app.features.home.HomeDrawerFragment] and
|
||||
* is displaying the space hierarchy, with some actions on Spaces.
|
||||
*
|
||||
* In the New App Layout this fragment will instead be displayed in a Bottom Sheet [SpaceListBottomSheet]
|
||||
* and will only display spaces that are direct children of the currently selected space (or root spaces if none)
|
||||
*/
|
||||
class SpaceListFragment @Inject constructor(
|
||||
private val spaceController: SpaceSummaryController
|
||||
) : VectorBaseFragment<FragmentSpaceListBinding>(), SpaceSummaryController.Callback {
|
||||
private val spaceController: SpaceSummaryController,
|
||||
private val newSpaceController: NewSpaceSummaryController,
|
||||
private val vectorFeatures: VectorFeatures,
|
||||
) : VectorBaseFragment<FragmentSpaceListBinding>(), SpaceSummaryController.Callback, NewSpaceSummaryController.Callback {
|
||||
|
||||
private lateinit var sharedActionViewModel: HomeSharedActionViewModel
|
||||
private lateinit var homeActivitySharedActionViewModel: HomeSharedActionViewModel
|
||||
private lateinit var roomListSharedActionViewModel: RoomListSharedActionViewModel
|
||||
private val viewModel: SpaceListViewModel by fragmentViewModel()
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSpaceListBinding {
|
||||
|
@ -54,10 +63,69 @@ class SpaceListFragment @Inject constructor(
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java)
|
||||
spaceController.callback = this
|
||||
homeActivitySharedActionViewModel = activityViewModelProvider[HomeSharedActionViewModel::class.java]
|
||||
roomListSharedActionViewModel = activityViewModelProvider[RoomListSharedActionViewModel::class.java]
|
||||
views.stateView.contentView = views.groupListView
|
||||
views.groupListView.configureWith(spaceController)
|
||||
setupSpaceController()
|
||||
observeViewEvents()
|
||||
}
|
||||
|
||||
private fun setupSpaceController() {
|
||||
if (vectorFeatures.isNewAppLayoutEnabled()) {
|
||||
enableDragAndDropForNewSpaceController()
|
||||
newSpaceController.callback = this
|
||||
views.groupListView.configureWith(newSpaceController)
|
||||
} else {
|
||||
enableDragAndDropForSpaceController()
|
||||
spaceController.callback = this
|
||||
views.groupListView.configureWith(spaceController)
|
||||
}
|
||||
}
|
||||
|
||||
private fun enableDragAndDropForNewSpaceController() {
|
||||
EpoxyTouchHelper.initDragging(newSpaceController)
|
||||
.withRecyclerView(views.groupListView)
|
||||
.forVerticalList()
|
||||
.withTarget(NewSpaceSummaryItem::class.java)
|
||||
.andCallbacks(object : EpoxyTouchHelper.DragCallbacks<NewSpaceSummaryItem>() {
|
||||
var toPositionM: Int? = null
|
||||
var fromPositionM: Int? = null
|
||||
var initialElevation: Float? = null
|
||||
|
||||
override fun onDragStarted(model: NewSpaceSummaryItem?, itemView: View?, adapterPosition: Int) {
|
||||
toPositionM = null
|
||||
fromPositionM = null
|
||||
model?.matrixItem?.id?.let {
|
||||
viewModel.handle(SpaceListAction.OnStartDragging(it, false))
|
||||
}
|
||||
itemView?.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||
initialElevation = itemView?.elevation
|
||||
itemView?.elevation = 6f
|
||||
}
|
||||
|
||||
override fun onDragReleased(model: NewSpaceSummaryItem?, itemView: View?) {
|
||||
if (toPositionM == null || fromPositionM == null) return
|
||||
val movedSpaceId = model?.matrixItem?.id ?: return
|
||||
viewModel.handle(SpaceListAction.MoveSpace(movedSpaceId, toPositionM!! - fromPositionM!!))
|
||||
}
|
||||
|
||||
override fun clearView(model: NewSpaceSummaryItem?, itemView: View?) {
|
||||
itemView?.elevation = initialElevation ?: 0f
|
||||
}
|
||||
|
||||
override fun onModelMoved(fromPosition: Int, toPosition: Int, modelBeingMoved: NewSpaceSummaryItem?, itemView: View?) {
|
||||
if (fromPositionM == null) {
|
||||
fromPositionM = fromPosition
|
||||
}
|
||||
if (toPositionM != toPosition) {
|
||||
toPositionM = toPosition
|
||||
itemView?.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun enableDragAndDropForSpaceController() {
|
||||
EpoxyTouchHelper.initDragging(spaceController)
|
||||
.withRecyclerView(views.groupListView)
|
||||
.forVerticalList()
|
||||
|
@ -100,14 +168,14 @@ class SpaceListFragment @Inject constructor(
|
|||
return model?.canDrag == true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
viewModel.observeViewEvents {
|
||||
when (it) {
|
||||
is SpaceListViewEvents.OpenSpaceSummary -> sharedActionViewModel.post(HomeActivitySharedAction.OpenSpacePreview(it.id))
|
||||
is SpaceListViewEvents.AddSpace -> sharedActionViewModel.post(HomeActivitySharedAction.AddSpace)
|
||||
is SpaceListViewEvents.OpenSpaceInvite -> sharedActionViewModel.post(HomeActivitySharedAction.OpenSpaceInvite(it.id))
|
||||
SpaceListViewEvents.CloseDrawer -> sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
|
||||
}
|
||||
private fun observeViewEvents() = viewModel.observeViewEvents {
|
||||
when (it) {
|
||||
is SpaceListViewEvents.OpenSpaceSummary -> homeActivitySharedActionViewModel.post(HomeActivitySharedAction.OpenSpacePreview(it.id))
|
||||
is SpaceListViewEvents.AddSpace -> homeActivitySharedActionViewModel.post(HomeActivitySharedAction.AddSpace)
|
||||
is SpaceListViewEvents.OpenSpaceInvite -> homeActivitySharedActionViewModel.post(HomeActivitySharedAction.OpenSpaceInvite(it.id))
|
||||
SpaceListViewEvents.CloseDrawer -> homeActivitySharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,11 +192,17 @@ class SpaceListFragment @Inject constructor(
|
|||
is Success -> views.stateView.state = StateView.State.Content
|
||||
else -> Unit
|
||||
}
|
||||
spaceController.update(state)
|
||||
|
||||
if (vectorFeatures.isNewAppLayoutEnabled()) {
|
||||
newSpaceController.update(state)
|
||||
} else {
|
||||
spaceController.update(state)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSpaceSelected(spaceSummary: RoomSummary?) {
|
||||
viewModel.handle(SpaceListAction.SelectSpace(spaceSummary))
|
||||
roomListSharedActionViewModel.post(RoomListSharedAction.CloseBottomSheet)
|
||||
}
|
||||
|
||||
override fun onSpaceInviteSelected(spaceSummary: RoomSummary) {
|
||||
|
@ -136,7 +210,7 @@ class SpaceListFragment @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onSpaceSettings(spaceSummary: RoomSummary) {
|
||||
sharedActionViewModel.post(HomeActivitySharedAction.ShowSpaceSettings(spaceSummary.roomId))
|
||||
homeActivitySharedActionViewModel.post(HomeActivitySharedAction.ShowSpaceSettings(spaceSummary.roomId))
|
||||
}
|
||||
|
||||
override fun onToggleExpand(spaceSummary: RoomSummary) {
|
||||
|
@ -148,6 +222,6 @@ class SpaceListFragment @Inject constructor(
|
|||
}
|
||||
|
||||
override fun sendFeedBack() {
|
||||
sharedActionViewModel.post(HomeActivitySharedAction.SendSpaceFeedBack)
|
||||
homeActivitySharedActionViewModel.post(HomeActivitySharedAction.SendSpaceFeedBack)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -194,7 +194,7 @@ class SpaceListViewModel @AssistedInject constructor(
|
|||
val moved = removeAt(index)
|
||||
add(index + action.delta, moved)
|
||||
},
|
||||
spaceOrderLocalEchos = updatedLocalEchos
|
||||
spaceOrderLocalEchos = updatedLocalEchos,
|
||||
)
|
||||
}
|
||||
session.coroutineScope.launch {
|
||||
|
@ -257,29 +257,29 @@ class SpaceListViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
combine(
|
||||
session.flow()
|
||||
.liveSpaceSummaries(params),
|
||||
session.flow().liveSpaceSummaries(params),
|
||||
session.accountDataService()
|
||||
.getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER))
|
||||
.asFlow()
|
||||
) { spaces, _ ->
|
||||
spaces
|
||||
}.execute { asyncSpaces ->
|
||||
val spaces = asyncSpaces.invoke().orEmpty()
|
||||
val rootSpaces = asyncSpaces.invoke().orEmpty().filter { it.flattenParentIds.isEmpty() }
|
||||
val orders = rootSpaces.associate {
|
||||
it.roomId to session.getRoom(it.roomId)
|
||||
?.roomAccountDataService()
|
||||
?.getAccountDataEvent(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)
|
||||
?.content.toModel<SpaceOrderContent>()
|
||||
?.safeOrder()
|
||||
}
|
||||
copy(
|
||||
asyncSpaces = asyncSpaces,
|
||||
spaces = spaces,
|
||||
rootSpacesOrdered = rootSpaces.sortedWith(TopLevelSpaceComparator(orders)),
|
||||
spaceOrderInfo = orders
|
||||
)
|
||||
}
|
||||
.execute { async ->
|
||||
val rootSpaces = async.invoke().orEmpty().filter { it.flattenParentIds.isEmpty() }
|
||||
val orders = rootSpaces.associate {
|
||||
it.roomId to session.getRoom(it.roomId)
|
||||
?.roomAccountDataService()
|
||||
?.getAccountDataEvent(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)
|
||||
?.content.toModel<SpaceOrderContent>()
|
||||
?.safeOrder()
|
||||
}
|
||||
copy(
|
||||
asyncSpaces = async,
|
||||
rootSpacesOrdered = rootSpaces.sortedWith(TopLevelSpaceComparator(orders)),
|
||||
spaceOrderInfo = orders
|
||||
)
|
||||
}
|
||||
|
||||
// clear local echos on update
|
||||
session.accountDataService()
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.util.MatrixItem
|
|||
data class SpaceListViewState(
|
||||
val myMxItem: Async<MatrixItem.UserItem> = Uninitialized,
|
||||
val asyncSpaces: Async<List<RoomSummary>> = Uninitialized,
|
||||
val spaces: List<RoomSummary> = emptyList(),
|
||||
val selectedSpace: RoomSummary? = null,
|
||||
val rootSpacesOrdered: List<RoomSummary>? = null,
|
||||
val spaceOrderInfo: Map<String, String?>? = null,
|
||||
|
|
11
vector/src/main/res/drawable/ic_plus.xml
Normal file
11
vector/src/main/res/drawable/ic_plus.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<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="M12.7822,4.2963C12.7822,3.8641 12.4318,3.5137 11.9996,3.5137C11.5673,3.5137 11.217,3.8641 11.217,4.2963V11.2173L4.2963,11.2173C3.8641,11.2173 3.5137,11.5676 3.5137,11.9999C3.5137,12.4321 3.8641,12.7825 4.2963,12.7825H11.217V19.7038C11.217,20.136 11.5673,20.4864 11.9996,20.4864C12.4318,20.4864 12.7822,20.136 12.7822,19.7038V12.7825H19.7038C20.136,12.7825 20.4864,12.4321 20.4864,11.9999C20.4864,11.5676 20.136,11.2173 19.7038,11.2173L12.7822,11.2173V4.2963Z"
|
||||
android:fillColor="#17191C"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
|
11
vector/src/main/res/drawable/new_space_home_background.xml
Normal file
11
vector/src/main/res/drawable/new_space_home_background.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<size android:width="40dp" android:height="40dp"/>
|
||||
|
||||
<solid android:color="?android:colorBackground" />
|
||||
|
||||
<corners android:radius="8dp" />
|
||||
|
||||
</shape>
|
|
@ -10,4 +10,4 @@
|
|||
|
||||
<corners android:radius="8dp" />
|
||||
|
||||
</shape>
|
||||
</shape>
|
||||
|
|
|
@ -1,101 +1,107 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<im.vector.app.core.platform.StateView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/stateView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:colorBackground">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/roomListView"
|
||||
<im.vector.app.core.platform.StateView
|
||||
android:id="@+id/stateView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="always"
|
||||
tools:itemCount="5"
|
||||
tools:listitem="@layout/item_room" />
|
||||
android:background="?android:colorBackground">
|
||||
|
||||
<im.vector.app.features.home.room.list.widget.NotifsFabMenuView
|
||||
android:id="@+id/createChatFabMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
app:layoutDescription="@xml/motion_scene_notifs_fab_menu"
|
||||
tools:showPaths="true"
|
||||
tools:visibility="visible" />
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/roomListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="always"
|
||||
tools:itemCount="5"
|
||||
tools:listitem="@layout/item_room" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/createChatRoomButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:accessibilityTraversalBefore="@id/roomListView"
|
||||
android:contentDescription="@string/a11y_create_direct_message"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_fab_add_chat"
|
||||
android:visibility="gone"
|
||||
app:maxImageSize="34dp"
|
||||
tools:layout_marginEnd="80dp"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/createGroupRoomButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:accessibilityTraversalBefore="@id/roomListView"
|
||||
android:contentDescription="@string/a11y_create_room"
|
||||
android:src="@drawable/ic_fab_add_room"
|
||||
android:visibility="gone"
|
||||
app:maxImageSize="32dp"
|
||||
tools:layout_marginEnd="144dp"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="bottom|end">
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/newLayoutOpenSpacesButton"
|
||||
style="@style/Widget.Vector.FloatingActionButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:accessibilityTraversalAfter="@id/newLayoutCreateChatButton"
|
||||
android:contentDescription="@string/a11y_open_spaces"
|
||||
android:src="@drawable/ic_open_spaces"
|
||||
<im.vector.app.features.home.room.list.widget.NotifsFabMenuView
|
||||
android:id="@+id/createChatFabMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="?attr/vctr_toolbar_background"
|
||||
app:fabSize="mini"
|
||||
app:layout_constraintBottom_toTopOf="@id/newLayoutCreateChatButton"
|
||||
app:layout_constraintEnd_toEndOf="@id/newLayoutCreateChatButton"
|
||||
app:layout_constraintStart_toStartOf="@id/newLayoutCreateChatButton"
|
||||
app:tint="?attr/colorPrimary"
|
||||
tools:visibility="visible"
|
||||
tools:targetApi="lollipop_mr1" />
|
||||
|
||||
app:layoutDescription="@xml/motion_scene_notifs_fab_menu"
|
||||
tools:showPaths="true"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/newLayoutCreateChatButton"
|
||||
style="@style/Widget.Vector.FloatingActionButton"
|
||||
android:id="@+id/createChatRoomButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:accessibilityTraversalBefore="@id/roomListView"
|
||||
android:contentDescription="@string/a11y_create_message"
|
||||
android:src="@drawable/ic_new_chat"
|
||||
android:contentDescription="@string/a11y_create_direct_message"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_fab_add_chat"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:visibility="visible"
|
||||
tools:targetApi="lollipop_mr1" />
|
||||
app:maxImageSize="34dp"
|
||||
tools:layout_marginEnd="80dp"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/createGroupRoomButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:accessibilityTraversalBefore="@id/roomListView"
|
||||
android:contentDescription="@string/a11y_create_room"
|
||||
android:src="@drawable/ic_fab_add_room"
|
||||
android:visibility="gone"
|
||||
app:maxImageSize="32dp"
|
||||
tools:layout_marginEnd="144dp"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</im.vector.app.core.platform.StateView>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="bottom|end">
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/newLayoutOpenSpacesButton"
|
||||
style="@style/Widget.Vector.FloatingActionButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:accessibilityTraversalAfter="@id/newLayoutCreateChatButton"
|
||||
android:contentDescription="@string/a11y_open_spaces"
|
||||
android:src="@drawable/ic_open_spaces"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="?attr/vctr_toolbar_background"
|
||||
app:fabSize="mini"
|
||||
app:layout_constraintBottom_toTopOf="@id/newLayoutCreateChatButton"
|
||||
app:layout_constraintEnd_toEndOf="@id/newLayoutCreateChatButton"
|
||||
app:layout_constraintStart_toStartOf="@id/newLayoutCreateChatButton"
|
||||
app:tint="?attr/colorPrimary"
|
||||
tools:targetApi="lollipop_mr1"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/newLayoutCreateChatButton"
|
||||
style="@style/Widget.Vector.FloatingActionButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:accessibilityTraversalBefore="@id/roomListView"
|
||||
android:contentDescription="@string/a11y_create_message"
|
||||
android:src="@drawable/ic_new_chat"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:targetApi="lollipop_mr1"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</im.vector.app.core.platform.StateView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
11
vector/src/main/res/layout/fragment_spaces_bottom_sheet.xml
Normal file
11
vector/src/main/res/layout/fragment_spaces_bottom_sheet.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/space_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</FrameLayout>
|
77
vector/src/main/res/layout/item_new_space.xml
Normal file
77
vector/src/main/res/layout/item_new_space.xml
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<im.vector.app.core.platform.CheckableConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="65dp"
|
||||
android:background="@drawable/bg_space_item"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
tools:viewBindingIgnore="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:duplicateParentState="true"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@sample/space_avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
style="@style/Widget.Vector.TextView.Subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar"
|
||||
app:layout_constraintEnd_toStartOf="@id/unread_counter"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Element Corp" />
|
||||
|
||||
<im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||
android:id="@+id/unread_counter"
|
||||
style="@style/Widget.Vector.TextView.Micro"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="center"
|
||||
android:minWidth="16dp"
|
||||
android:minHeight="16dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:textColor="?colorOnError"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toStartOf="@id/chevron"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:background="@drawable/bg_unread_highlight"
|
||||
tools:text="147"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chevron"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="21dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:tint="?vctr_content_secondary"
|
||||
tools:ignore="MissingPrefix" />
|
||||
|
||||
</im.vector.app.core.platform.CheckableConstraintLayout>
|
54
vector/src/main/res/layout/item_new_space_add.xml
Normal file
54
vector/src/main/res/layout/item_new_space_add.xml
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<im.vector.app.core.platform.CheckableConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/itemGroupLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="65dp"
|
||||
android:background="@drawable/bg_space_item"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
tools:viewBindingIgnore="true">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="26dp"
|
||||
android:background="?attr/vctr_system"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/plus"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:background="@drawable/rounded_rect_shape_8"
|
||||
android:backgroundTint="#4D8D97A5"
|
||||
android:duplicateParentState="true"
|
||||
android:importantForAccessibility="no"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_plus"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/groupNameView"
|
||||
style="@style/Widget.Vector.TextView.Subtitle.Medium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="@string/create_space"
|
||||
android:textColor="?vctr_message_text_color"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/plus"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</im.vector.app.core.platform.CheckableConstraintLayout>
|
16
vector/src/main/res/layout/item_new_space_list_header.xml
Normal file
16
vector/src/main/res/layout/item_new_space_list_header.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
style="@style/TextAppearance.Vector.Body.Medium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_space_item"
|
||||
android:ellipsize="middle"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/change_space"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="?vctr_content_tertiary"
|
||||
android:textSize="14sp"
|
||||
tools:viewBindingIgnore="true" />
|
|
@ -137,6 +137,7 @@
|
|||
|
||||
<!-- Home Screen -->
|
||||
<string name="all_chats">All Chats</string>
|
||||
<string name="change_space">Change Space</string>
|
||||
|
||||
<!-- Last seen time -->
|
||||
|
||||
|
|
Loading…
Reference in a new issue