diff --git a/vector/src/main/java/im/vector/app/features/home/InvitesAdapter.kt b/vector/src/main/java/im/vector/app/features/home/InvitesAdapter.kt new file mode 100644 index 0000000000..3aeadeb3c6 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/InvitesAdapter.kt @@ -0,0 +1,68 @@ +/* + * 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 + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import im.vector.app.R +import im.vector.app.databinding.ListItemInviteBinding +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.util.toMatrixItem + +class InvitesAdapter( + private val avatarRenderer: AvatarRenderer, + private val inviteUserTask: ((String) -> String?)?, +) : RecyclerView.Adapter() { + + private val invites = mutableListOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = ListItemInviteBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(invites[position]) + } + + override fun getItemCount() = invites.size + + @SuppressLint("NotifyDataSetChanged") + fun updateList(invites: List) { + this.invites.clear() + this.invites.addAll(invites) + notifyDataSetChanged() + } + + inner class ViewHolder(private val binding: ListItemInviteBinding) : RecyclerView.ViewHolder(binding.root) { + + fun bind(invite: RoomSummary) { + avatarRenderer.render(invite.toMatrixItem(), binding.avatar) + binding.name.text = invite.name + invite.inviterId?.let { + val inviterName = inviteUserTask?.invoke(it) + if (inviterName != null) { + binding.invitedBy.text = binding.root.context.getString(R.string.invited_by, inviterName) + } else { + binding.invitedBy.text = "" + } + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/InvitesBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/InvitesBottomSheet.kt new file mode 100644 index 0000000000..ff28a3bc4e --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/InvitesBottomSheet.kt @@ -0,0 +1,61 @@ +/* + * 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 + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import im.vector.app.databinding.BottomSheetInvitesBinding +import org.matrix.android.sdk.api.session.room.model.RoomSummary + +class InvitesBottomSheet( + private val invites: List, + private val avatarRenderer: AvatarRenderer, + private val inviteUserTask: ((String) -> String?)? +) : BottomSheetDialogFragment() { + + private lateinit var binding: BottomSheetInvitesBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = BottomSheetInvitesBinding.inflate(inflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupInvitesList() + } + + private fun setupInvitesList() { + val adapter = InvitesAdapter(avatarRenderer, inviteUserTask) + val layoutManager = LinearLayoutManager(context) + binding.invitesList.adapter = adapter + binding.invitesList.layoutManager = layoutManager + adapter.updateList(invites) + } + + companion object { + const val TAG = "InvitesBottomSheet" + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/SpaceListModalFragment.kt b/vector/src/main/java/im/vector/app/features/home/SpaceListModalFragment.kt index cf8dcd2479..291e3936d6 100644 --- a/vector/src/main/java/im/vector/app/features/home/SpaceListModalFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/SpaceListModalFragment.kt @@ -20,6 +20,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.view.allViews import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.fragmentViewModel @@ -58,6 +59,7 @@ class SpaceListModalFragment : VectorBaseFragment sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java) setupRecyclerView() setupAddSpace() + setupInvites() observeSpaceChange() } @@ -82,6 +84,17 @@ class SpaceListModalFragment : VectorBaseFragment } } + private fun setupInvites() { + binding.invitesGroup.referencedIds.map { binding.root.findViewById(it) }.forEach { + it.setOnClickListener { + withState(viewModel) { state -> + val invitesBottomSheet = InvitesBottomSheet(state.inviteSpaces.orEmpty(), avatarRenderer, state.inviteUserTask) + invitesBottomSheet.show(requireActivity().supportFragmentManager, InvitesBottomSheet.TAG) + } + } + } + } + private fun observeSpaceChange() = sharedActionViewModel.space.observe(viewLifecycleOwner) { viewModel.setSpace(it) binding.headerText.isVisible = it == null diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewModel.kt index e38aaa38be..1f6945a749 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewModel.kt @@ -51,6 +51,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.session.getUser import org.matrix.android.sdk.api.session.group.groupSummaryQueryParams import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes @@ -295,6 +296,9 @@ class SpaceListViewModel @AssistedInject constructor(@Assisted initialState: Spa val rootSpaces = async.invoke().orEmpty().filter { it.flattenParentIds.isEmpty() } val displaySpaces = (currentSpaceChildren ?: rootSpaces).filter { it.inviterId == null } val inviteSpaces = (currentSpaceChildren ?: rootSpaces).filter { it.inviterId != null } + val inviteUserTask: (String) -> String? = { + session.getUser(it)?.displayName + } val orders = displaySpaces.associate { it.roomId to session.getRoom(it.roomId) ?.roomAccountDataService() @@ -307,6 +311,7 @@ class SpaceListViewModel @AssistedInject constructor(@Assisted initialState: Spa rootSpacesOrdered = displaySpaces.sortedWith(TopLevelSpaceComparator(orders)), inviteSpaces = inviteSpaces.sortedWith(TopLevelSpaceComparator(orders)), inviteCount = inviteSpaces.size, + inviteUserTask = inviteUserTask, spaceOrderInfo = orders ) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt index daeabdf34f..56fae3ea04 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt @@ -36,5 +36,6 @@ data class SpaceListViewState( val legacyGroups: List? = null, val expandedStates: Map = emptyMap(), val inviteCount: Int = 0, + val inviteUserTask: ((String) -> String?)? = null, val homeAggregateCount: RoomAggregateNotificationCount = RoomAggregateNotificationCount(0, 0) ) : MavericksState diff --git a/vector/src/main/res/layout/bottom_sheet_invites.xml b/vector/src/main/res/layout/bottom_sheet_invites.xml new file mode 100644 index 0000000000..3b6078c967 --- /dev/null +++ b/vector/src/main/res/layout/bottom_sheet_invites.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/vector/src/main/res/layout/list_item_invite.xml b/vector/src/main/res/layout/list_item_invite.xml new file mode 100644 index 0000000000..4a867893e2 --- /dev/null +++ b/vector/src/main/res/layout/list_item_invite.xml @@ -0,0 +1,44 @@ + + + + + + + + + + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 1622637853..54fe2338e0 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3045,4 +3045,5 @@ Add spaces to group your chats No subspaces yet Add subspaces to group your chats + Space Invites