From b1af01b9eacb02873aaff803864c8fa839a3f9be Mon Sep 17 00:00:00 2001 From: Taras Smakula <tarassmakula@gmail.com> Date: Fri, 8 Sep 2023 17:26:41 +0300 Subject: [PATCH] Add invites and knoks to photos --- .../feature/circles/CirclesViewModel.kt | 2 +- .../feature/circles/list/CirclesViewHolder.kt | 2 +- .../feature/groups/GroupsDataSource.kt | 22 +----- .../feature/groups/list/GroupViewHolder.kt | 5 +- .../circles/mapping/RoomSummaryMapping.kt | 6 +- .../org/futo/circles/model/GroupListItem.kt | 4 +- app/src/main/res/values/strings.xml | 3 - auth/src/main/res/values/strings.xml | 1 - .../core/mapping/RoomSummaryMapping.kt | 20 ++++- .../circles/core/model/GalleryListItem.kt | 27 ++++++- .../gallery/rooms/PickGalleryFragment.kt | 6 +- .../gallery/rooms/PickGalleryViewModel.kt | 7 +- .../gallery/rooms/list/GalleryViewHolder.kt | 77 ++++++++++++++++-- ...stAdapter.kt => PickGalleryListAdapter.kt} | 13 +-- .../res/layout/list_item_invited_gallery.xml | 79 +++++++++++++++++++ ...llery.xml => list_item_joined_gallery.xml} | 0 .../res/layout/list_item_request_gallery.xml | 79 +++++++++++++++++++ core/src/main/res/values/strings.xml | 4 + .../gallery/feature/PhotosDataSource.kt | 67 +++++++++++++--- .../circles/gallery/feature/PhotosFragment.kt | 29 +++++-- .../gallery/feature/PhotosListAdapter.kt | 58 ++++++++++++++ .../gallery/feature/PhotosViewModel.kt | 38 ++++++++- .../res/layout/list_item_select_gallery.xml | 2 +- 23 files changed, 478 insertions(+), 73 deletions(-) rename core/src/main/java/org/futo/circles/core/picker/gallery/rooms/list/{PhotosListAdapter.kt => PickGalleryListAdapter.kt} (59%) create mode 100644 core/src/main/res/layout/list_item_invited_gallery.xml rename core/src/main/res/layout/{list_item_gallery.xml => list_item_joined_gallery.xml} (100%) create mode 100644 core/src/main/res/layout/list_item_request_gallery.xml create mode 100644 gallery/src/main/java/org/futo/circles/gallery/feature/PhotosListAdapter.kt diff --git a/app/src/main/java/org/futo/circles/feature/circles/CirclesViewModel.kt b/app/src/main/java/org/futo/circles/feature/circles/CirclesViewModel.kt index ace0a4f49..876ee23a9 100644 --- a/app/src/main/java/org/futo/circles/feature/circles/CirclesViewModel.kt +++ b/app/src/main/java/org/futo/circles/feature/circles/CirclesViewModel.kt @@ -12,7 +12,7 @@ import javax.inject.Inject @HiltViewModel class CirclesViewModel @Inject constructor( - private val dataSource: CirclesDataSource, + dataSource: CirclesDataSource, private val inviteRequestsDataSource: InviteRequestsDataSource ) : ViewModel() { diff --git a/app/src/main/java/org/futo/circles/feature/circles/list/CirclesViewHolder.kt b/app/src/main/java/org/futo/circles/feature/circles/list/CirclesViewHolder.kt index e21d9cb53..f87de8480 100644 --- a/app/src/main/java/org/futo/circles/feature/circles/list/CirclesViewHolder.kt +++ b/app/src/main/java/org/futo/circles/feature/circles/list/CirclesViewHolder.kt @@ -99,7 +99,7 @@ class InvitedCircleViewHolder( setIcon(ivCircle, data.info.avatarUrl, data.info.title) setTitle(tvCircleTitle, data.info.title) binding.tvInvitedBy.text = - context.getString(R.string.invited_by_format, data.inviterName) + context.getString(org.futo.circles.core.R.string.invited_by_format, data.inviterName) } } } diff --git a/app/src/main/java/org/futo/circles/feature/groups/GroupsDataSource.kt b/app/src/main/java/org/futo/circles/feature/groups/GroupsDataSource.kt index 03240110c..74c304469 100644 --- a/app/src/main/java/org/futo/circles/feature/groups/GroupsDataSource.kt +++ b/app/src/main/java/org/futo/circles/feature/groups/GroupsDataSource.kt @@ -5,11 +5,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.withContext -import org.futo.circles.core.extensions.createResult import org.futo.circles.core.mapping.toRoomInfo import org.futo.circles.core.model.GROUP_TYPE import org.futo.circles.core.provider.MatrixSessionProvider -import org.futo.circles.core.room.RoomRelationsBuilder import org.futo.circles.core.utils.UserUtils import org.futo.circles.mapping.toInviteGroupListItem import org.futo.circles.mapping.toJoinedGroupListItem @@ -22,18 +20,12 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import javax.inject.Inject -class GroupsDataSource @Inject constructor( - private val roomRelationsBuilder: RoomRelationsBuilder -) { - - val session by lazy { - MatrixSessionProvider.currentSession - ?: throw IllegalArgumentException("session is not created") - } +class GroupsDataSource @Inject constructor() { fun getGroupsFlow() = combine( - session.roomService().getRoomSummariesLive(roomSummaryQueryParams()).asFlow(), - session.roomService().getChangeMembershipsLive().asFlow() + MatrixSessionProvider.getSessionOrThrow().roomService() + .getRoomSummariesLive(roomSummaryQueryParams()).asFlow(), + MatrixSessionProvider.getSessionOrThrow().roomService().getChangeMembershipsLive().asFlow() ) { roomSummaries, _ -> withContext(Dispatchers.IO) { filterGroups(roomSummaries) } }.distinctUntilChanged() @@ -78,10 +70,4 @@ class GroupsDataSource @Inject constructor( } return requests } - - - suspend fun acceptInvite(roomId: String) = createResult { - MatrixSessionProvider.currentSession?.roomService()?.joinRoom(roomId) - roomRelationsBuilder.setInvitedGroupRelations(roomId) - } } \ No newline at end of file diff --git a/app/src/main/java/org/futo/circles/feature/groups/list/GroupViewHolder.kt b/app/src/main/java/org/futo/circles/feature/groups/list/GroupViewHolder.kt index 3c129a53b..5fdd949d2 100644 --- a/app/src/main/java/org/futo/circles/feature/groups/list/GroupViewHolder.kt +++ b/app/src/main/java/org/futo/circles/feature/groups/list/GroupViewHolder.kt @@ -118,7 +118,8 @@ class InvitedGroupViewHolder( setIcon(binding.ivGroup, data.info.avatarUrl, data.info.title) setIsEncrypted(binding.ivLock, data.isEncrypted) setTitle(binding.tvGroupTitle, data.info.title) - binding.tvInviterName.text = context.getString(R.string.invited_by_format, data.inviterName) + binding.tvInviterName.text = + context.getString(org.futo.circles.core.R.string.invited_by_format, data.inviterName) } } @@ -142,7 +143,7 @@ class RequestGroupViewHolder( with(binding) { setIcon(ivGroup, data.info.avatarUrl, data.info.title) binding.tvRequestUserId.text = context.getString( - R.string.requested_to_join_format, data.requesterName + org.futo.circles.core.R.string.requested_to_join_format, data.requesterName ) binding.tvRoomName.text = data.info.title } diff --git a/app/src/main/java/org/futo/circles/mapping/RoomSummaryMapping.kt b/app/src/main/java/org/futo/circles/mapping/RoomSummaryMapping.kt index 843961929..e74b5e692 100644 --- a/app/src/main/java/org/futo/circles/mapping/RoomSummaryMapping.kt +++ b/app/src/main/java/org/futo/circles/mapping/RoomSummaryMapping.kt @@ -1,6 +1,6 @@ package org.futo.circles.mapping -import org.futo.circles.core.extensions.notEmptyDisplayName +import org.futo.circles.core.mapping.getInviterName import org.futo.circles.core.mapping.toRoomInfo import org.futo.circles.core.model.RoomInfo import org.futo.circles.core.provider.MatrixSessionProvider @@ -11,7 +11,6 @@ import org.futo.circles.model.JoinedCircleListItem import org.futo.circles.model.JoinedGroupListItem import org.futo.circles.model.TimelineRoomListItem import org.matrix.android.sdk.api.session.getRoomSummary -import org.matrix.android.sdk.api.session.getUserOrDefault 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 @@ -52,9 +51,6 @@ fun RoomSummary.toInviteCircleListItem() = InvitedCircleListItem( private fun RoomSummary.getFollowersCount(): Int = getTimelineRoomFor(roomId)?.roomSummary()?.otherMemberIds?.size ?: 0 -private fun RoomSummary.getInviterName() = - MatrixSessionProvider.currentSession?.getUserOrDefault(inviterId ?: "")?.notEmptyDisplayName() - ?: "" private fun RoomSummary.getCircleUnreadMessagesCount(): Int { var unreadInCircle = 0 diff --git a/app/src/main/java/org/futo/circles/model/GroupListItem.kt b/app/src/main/java/org/futo/circles/model/GroupListItem.kt index 50471ba43..f41310a9b 100644 --- a/app/src/main/java/org/futo/circles/model/GroupListItem.kt +++ b/app/src/main/java/org/futo/circles/model/GroupListItem.kt @@ -24,12 +24,12 @@ data class InvitedGroupListItem( override val id: String, override val info: RoomInfo, val isEncrypted: Boolean, - val inviterName: String, + val inviterName: String ) : GroupListItem(id, info, Membership.INVITE) data class RequestGroupListItem( override val id: String, override val info: RoomInfo, val requesterName: String, - val requesterId: String, + val requesterId: String ) : GroupListItem(id, info, Membership.KNOCK) \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 704576132..666803df9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -84,10 +84,7 @@ <string name="remove_from_this_circle_but_do_not_unfollow">Remove from this circle, but do not unfollow</string> <string name="unfollow_completely_remove_from_all_circles">Unfollow completely (remove from all circles)</string> <string name="unfollow">Unfollow</string> - <string name="decline">Decline</string> - <string name="invited_by_format">Invited by %s</string> <string name="requested_to_follow_format">%s requested to follow</string> - <string name="requested_to_join_format">%s requested to join</string> <string name="requested_to_join">Requested to join</string> <string name="select_circles_in_which_you_want_to_follow_this_timeline">Select circles in which you want to follow this timeline</string> <string name="accept_invite">Accept invite</string> diff --git a/auth/src/main/res/values/strings.xml b/auth/src/main/res/values/strings.xml index 756bc5a1f..1ca60b490 100644 --- a/auth/src/main/res/values/strings.xml +++ b/auth/src/main/res/values/strings.xml @@ -52,7 +52,6 @@ <string name="discard_current_registration_progress">Discard current registration progress?</string> <string name="domain">Domain</string> <string name="accept_terms_to_continue">Accept terms to continue</string> - <string name="accept">Accept</string> <string name="username">Username</string> <string name="at_symbol">\@</string> <string name="user_id_separator">:</string> diff --git a/core/src/main/java/org/futo/circles/core/mapping/RoomSummaryMapping.kt b/core/src/main/java/org/futo/circles/core/mapping/RoomSummaryMapping.kt index 3f54b77ef..206481728 100644 --- a/core/src/main/java/org/futo/circles/core/mapping/RoomSummaryMapping.kt +++ b/core/src/main/java/org/futo/circles/core/mapping/RoomSummaryMapping.kt @@ -1,8 +1,12 @@ package org.futo.circles.core.mapping -import org.futo.circles.core.model.GalleryListItem +import org.futo.circles.core.extensions.notEmptyDisplayName +import org.futo.circles.core.model.InvitedGalleryListItem +import org.futo.circles.core.model.JoinedGalleryListItem import org.futo.circles.core.model.RoomInfo import org.futo.circles.core.model.SelectableRoomListItem +import org.futo.circles.core.provider.MatrixSessionProvider +import org.matrix.android.sdk.api.session.getUserOrDefault import org.matrix.android.sdk.api.session.room.model.RoomSummary fun RoomSummary.nameOrId() = displayName.takeIf { it.isNotEmpty() } ?: roomId @@ -18,7 +22,17 @@ fun RoomSummary.toSelectableRoomListItem(selected: Boolean = false) = Selectable isSelected = selected ) -fun RoomSummary.toGalleryListItem() = GalleryListItem( +fun RoomSummary.toJoinedGalleryListItem() = JoinedGalleryListItem( id = roomId, info = toRoomInfo() -) \ No newline at end of file +) + +fun RoomSummary.toInvitedGalleryListItem() = InvitedGalleryListItem( + id = roomId, + info = toRoomInfo(), + inviterName = getInviterName() +) + +fun RoomSummary.getInviterName() = + MatrixSessionProvider.currentSession?.getUserOrDefault(inviterId ?: "")?.notEmptyDisplayName() + ?: "" \ No newline at end of file diff --git a/core/src/main/java/org/futo/circles/core/model/GalleryListItem.kt b/core/src/main/java/org/futo/circles/core/model/GalleryListItem.kt index 5f32ac5be..9f904c5fd 100644 --- a/core/src/main/java/org/futo/circles/core/model/GalleryListItem.kt +++ b/core/src/main/java/org/futo/circles/core/model/GalleryListItem.kt @@ -1,9 +1,28 @@ package org.futo.circles.core.model import org.futo.circles.core.list.IdEntity -import org.futo.circles.core.model.RoomInfo +import org.matrix.android.sdk.api.session.room.model.Membership -data class GalleryListItem( +sealed class GalleryListItem( override val id: String, - val info: RoomInfo -) : IdEntity<String> \ No newline at end of file + open val info: RoomInfo, + open val membership: Membership +) : IdEntity<String> + +data class JoinedGalleryListItem( + override val id: String, + override val info: RoomInfo +) : GalleryListItem(id, info, Membership.JOIN) + +data class InvitedGalleryListItem( + override val id: String, + override val info: RoomInfo, + val inviterName: String, +) : GalleryListItem(id, info, Membership.INVITE) + +data class RequestGalleryListItem( + override val id: String, + override val info: RoomInfo, + val requesterName: String, + val requesterId: String +) : GalleryListItem(id, info, Membership.KNOCK) \ No newline at end of file diff --git a/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/PickGalleryFragment.kt b/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/PickGalleryFragment.kt index 62c561b00..d41908252 100644 --- a/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/PickGalleryFragment.kt +++ b/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/PickGalleryFragment.kt @@ -10,7 +10,7 @@ import org.futo.circles.core.R import org.futo.circles.core.databinding.FragmentPickGalleryBinding import org.futo.circles.core.extensions.observeData import org.futo.circles.core.picker.gallery.PickGalleryMediaViewModel -import org.futo.circles.core.picker.gallery.rooms.list.PhotosListAdapter +import org.futo.circles.core.picker.gallery.rooms.list.PickGalleryListAdapter @AndroidEntryPoint class PickGalleryFragment : Fragment(R.layout.fragment_pick_gallery) { @@ -20,7 +20,9 @@ class PickGalleryFragment : Fragment(R.layout.fragment_pick_gallery) { private val binding by viewBinding(FragmentPickGalleryBinding::bind) private val listAdapter by lazy { - PhotosListAdapter(onRoomClicked = { gallery -> parentViewModel.onGalleryChosen(gallery.id) }) + PickGalleryListAdapter(onRoomClicked = { gallery -> + parentViewModel.onGalleryChosen(gallery.id) + }) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/PickGalleryViewModel.kt b/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/PickGalleryViewModel.kt index 00293c4cd..59fb53647 100644 --- a/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/PickGalleryViewModel.kt +++ b/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/PickGalleryViewModel.kt @@ -3,9 +3,10 @@ package org.futo.circles.core.picker.gallery.rooms import androidx.lifecycle.ViewModel import androidx.lifecycle.map import dagger.hilt.android.lifecycle.HiltViewModel -import org.futo.circles.core.mapping.toGalleryListItem +import org.futo.circles.core.mapping.toJoinedGalleryListItem import org.futo.circles.core.model.GALLERY_TYPE import org.futo.circles.core.model.GalleryListItem +import org.futo.circles.core.model.JoinedGalleryListItem import org.futo.circles.core.provider.MatrixSessionProvider import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary @@ -19,10 +20,10 @@ class PickGalleryViewModel @Inject constructor() : ViewModel() { ?.getRoomSummariesLive(roomSummaryQueryParams()) ?.map { list -> filterGalleries(list) } - private fun filterGalleries(list: List<RoomSummary>): List<GalleryListItem> { + private fun filterGalleries(list: List<RoomSummary>): List<JoinedGalleryListItem> { return list.mapNotNull { summary -> if (summary.roomType == GALLERY_TYPE && summary.membership == Membership.JOIN) { - summary.toGalleryListItem() + summary.toJoinedGalleryListItem() } else null } } diff --git a/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/list/GalleryViewHolder.kt b/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/list/GalleryViewHolder.kt index 5691802b6..5c8977a1c 100644 --- a/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/list/GalleryViewHolder.kt +++ b/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/list/GalleryViewHolder.kt @@ -1,30 +1,97 @@ package org.futo.circles.core.picker.gallery.rooms.list +import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import org.futo.circles.core.databinding.ListItemGalleryBinding +import org.futo.circles.core.R +import org.futo.circles.core.databinding.ListItemInvitedGalleryBinding +import org.futo.circles.core.databinding.ListItemJoinedGalleryBinding +import org.futo.circles.core.databinding.ListItemRequestGalleryBinding import org.futo.circles.core.extensions.loadProfileIcon import org.futo.circles.core.extensions.onClick import org.futo.circles.core.list.ViewBindingHolder +import org.futo.circles.core.list.context import org.futo.circles.core.model.GalleryListItem +import org.futo.circles.core.model.InvitedGalleryListItem +import org.futo.circles.core.model.JoinedGalleryListItem +import org.futo.circles.core.model.RequestGalleryListItem -class GalleryViewHolder( + +abstract class GalleryViewHolder(view: View) : RecyclerView.ViewHolder(view) { + abstract fun bind(data: GalleryListItem) +} + +class JoinedGalleryViewHolder( parent: ViewGroup, onGalleryClicked: (Int) -> Unit -) : RecyclerView.ViewHolder(inflate(parent, ListItemGalleryBinding::inflate)) { +) : GalleryViewHolder(inflate(parent, ListItemJoinedGalleryBinding::inflate)) { private companion object : ViewBindingHolder - private val binding = baseBinding as ListItemGalleryBinding + private val binding = baseBinding as ListItemJoinedGalleryBinding init { onClick(itemView) { position -> onGalleryClicked(position) } } - fun bind(data: GalleryListItem) { + override fun bind(data: GalleryListItem) { + if (data !is JoinedGalleryListItem) return + with(binding) { ivGalleryImage.loadProfileIcon(data.info.avatarUrl, "") tvGalleryName.text = data.info.title } } +} + +class InvitedGalleryViewHolder( + parent: ViewGroup, + onInviteClicked: (Int, Boolean) -> Unit +) : GalleryViewHolder(inflate(parent, ListItemInvitedGalleryBinding::inflate)) { + + private companion object : ViewBindingHolder + + private val binding = baseBinding as ListItemInvitedGalleryBinding + + init { + onClick(binding.btnAccept) { position -> onInviteClicked(position, true) } + onClick(binding.btnDecline) { position -> onInviteClicked(position, false) } + } + + override fun bind(data: GalleryListItem) { + if (data !is InvitedGalleryListItem) return + + with(binding) { + tvGalleryTitle.text = data.info.title + ivGallery.loadProfileIcon(data.info.avatarUrl, data.info.title) + tvInviterName.text = context.getString(R.string.invited_by_format, data.inviterName) + } + } +} + +class RequestGalleryViewHolder( + parent: ViewGroup, + onRequestClicked: (Int, Boolean) -> Unit +) : GalleryViewHolder(inflate(parent, ListItemRequestGalleryBinding::inflate)) { + + private companion object : ViewBindingHolder + + private val binding = baseBinding as ListItemRequestGalleryBinding + + init { + onClick(binding.btnInvite) { position -> onRequestClicked(position, true) } + onClick(binding.btnDecline) { position -> onRequestClicked(position, false) } + } + + override fun bind(data: GalleryListItem) { + if (data !is RequestGalleryListItem) return + + with(binding) { + binding.tvRequestUserId.text = context.getString( + R.string.requested_to_join_format, data.requesterName + ) + binding.tvRoomName.text = data.info.title + ivGallery.loadProfileIcon(data.info.avatarUrl, data.info.title) + } + } } \ No newline at end of file diff --git a/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/list/PhotosListAdapter.kt b/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/list/PickGalleryListAdapter.kt similarity index 59% rename from core/src/main/java/org/futo/circles/core/picker/gallery/rooms/list/PhotosListAdapter.kt rename to core/src/main/java/org/futo/circles/core/picker/gallery/rooms/list/PickGalleryListAdapter.kt index 2909e4ad7..331bbf201 100644 --- a/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/list/PhotosListAdapter.kt +++ b/core/src/main/java/org/futo/circles/core/picker/gallery/rooms/list/PickGalleryListAdapter.kt @@ -1,22 +1,25 @@ package org.futo.circles.core.picker.gallery.rooms.list + import android.view.ViewGroup import org.futo.circles.core.list.BaseRvAdapter import org.futo.circles.core.model.GalleryListItem -class PhotosListAdapter( - private val onRoomClicked: (GalleryListItem) -> Unit -) : BaseRvAdapter<GalleryListItem, GalleryViewHolder>(DefaultIdEntityCallback()) { + +class PickGalleryListAdapter( + private val onRoomClicked: (GalleryListItem) -> Unit, +) : BaseRvAdapter<GalleryListItem, JoinedGalleryViewHolder>(DefaultIdEntityCallback()) { + override fun onCreateViewHolder( parent: ViewGroup, viewType: Int - ) = GalleryViewHolder( + ) = JoinedGalleryViewHolder( parent = parent, onGalleryClicked = { position -> onRoomClicked(getItem(position)) } ) - override fun onBindViewHolder(holder: GalleryViewHolder, position: Int) { + override fun onBindViewHolder(holder: JoinedGalleryViewHolder, position: Int) { holder.bind(getItem(position)) } diff --git a/core/src/main/res/layout/list_item_invited_gallery.xml b/core/src/main/res/layout/list_item_invited_gallery.xml new file mode 100644 index 000000000..94d9797d4 --- /dev/null +++ b/core/src/main/res/layout/list_item_invited_gallery.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?selectableItemBackground" + android:clickable="true" + android:focusable="true" + android:padding="4dp"> + + + <com.google.android.material.imageview.ShapeableImageView + android:id="@+id/ivGallery" + android:layout_width="@dimen/group_icon_size" + android:layout_height="@dimen/group_icon_size" + android:scaleType="centerCrop" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.GroupIconRadius" + tools:src="@color/blue" /> + + + <TextView + android:id="@+id/tvGalleryTitle" + style="@style/title2" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginTop="4dp" + android:ellipsize="end" + android:lines="1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/ivGallery" + app:layout_constraintTop_toTopOf="parent" + tools:text="texsdt" /> + + + <TextView + android:id="@+id/tvInviterName" + style="@style/subheadline" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:ellipsize="end" + android:lines="1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@id/tvGalleryTitle" + app:layout_constraintTop_toBottomOf="@id/tvGalleryTitle" + tools:text="texsdt" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/btnAccept" + style="@style/AccentButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginEnd="16dp" + android:text="@string/accept" + android:textSize="12sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/btnDecline" + app:layout_constraintStart_toStartOf="@id/tvGalleryTitle" + app:layout_constraintTop_toBottomOf="@+id/tvInviterName" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/btnDecline" + style="@style/NegativeButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:text="@string/decline" + android:textSize="12sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/btnAccept" + app:layout_constraintTop_toBottomOf="@+id/tvInviterName" /> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/core/src/main/res/layout/list_item_gallery.xml b/core/src/main/res/layout/list_item_joined_gallery.xml similarity index 100% rename from core/src/main/res/layout/list_item_gallery.xml rename to core/src/main/res/layout/list_item_joined_gallery.xml diff --git a/core/src/main/res/layout/list_item_request_gallery.xml b/core/src/main/res/layout/list_item_request_gallery.xml new file mode 100644 index 000000000..6a83fb431 --- /dev/null +++ b/core/src/main/res/layout/list_item_request_gallery.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?selectableItemBackground" + android:clickable="true" + android:focusable="true" + android:padding="4dp"> + + <com.google.android.material.imageview.ShapeableImageView + android:id="@+id/ivGallery" + android:layout_width="@dimen/group_icon_size" + android:layout_height="@dimen/group_icon_size" + android:scaleType="centerCrop" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.GroupIconRadius" + tools:src="@color/blue" /> + + + <TextView + android:id="@+id/tvRequestUserId" + style="@style/body" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:ellipsize="end" + android:lines="1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/ivGallery" + app:layout_constraintTop_toTopOf="@id/ivGallery" + app:layout_constraintVertical_chainStyle="packed" + tools:text="texsdt" /> + + <TextView + android:id="@+id/tvRoomName" + style="@style/body" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:ellipsize="end" + android:lines="1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/ivGallery" + app:layout_constraintTop_toBottomOf="@+id/tvRequestUserId" + app:layout_constraintVertical_chainStyle="packed" + tools:text="texsdt" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/btnInvite" + style="@style/AccentButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginEnd="16dp" + android:text="@string/invite" + android:textSize="12sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/btnDecline" + app:layout_constraintStart_toStartOf="@id/tvRequestUserId" + app:layout_constraintTop_toBottomOf="@id/tvRoomName" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/btnDecline" + style="@style/NegativeButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:text="@string/decline" + android:textSize="12sp" + app:layout_constraintBottom_toBottomOf="@id/btnInvite" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/btnInvite" + app:layout_constraintTop_toTopOf="@id/btnInvite" /> + + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index e91300ed4..5c4f97558 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -125,5 +125,9 @@ <string name="gallery_updated">Gallery updated</string> <string name="last_updated_formatter">Last updated %s</string> <string name="delete_gallery_message">Are you sure you want to remove this gallery?</string> + <string name="accept">Accept</string> + <string name="decline">Decline</string> + <string name="requested_to_join_format">%s requested to join</string> + <string name="invited_by_format">Invited by %s</string> </resources> \ No newline at end of file diff --git a/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosDataSource.kt b/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosDataSource.kt index bd0116872..092931b58 100644 --- a/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosDataSource.kt +++ b/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosDataSource.kt @@ -1,10 +1,20 @@ package org.futo.circles.gallery.feature -import androidx.lifecycle.map -import org.futo.circles.core.mapping.toGalleryListItem +import androidx.lifecycle.asFlow +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.withContext +import org.futo.circles.core.mapping.toInvitedGalleryListItem +import org.futo.circles.core.mapping.toJoinedGalleryListItem +import org.futo.circles.core.mapping.toRoomInfo import org.futo.circles.core.model.GALLERY_TYPE import org.futo.circles.core.model.GalleryListItem +import org.futo.circles.core.model.RequestGalleryListItem import org.futo.circles.core.provider.MatrixSessionProvider +import org.futo.circles.core.utils.UserUtils +import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams 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.roomSummaryQueryParams @@ -12,15 +22,52 @@ import javax.inject.Inject class PhotosDataSource @Inject constructor() { - fun getGalleriesLiveData() = MatrixSessionProvider.currentSession?.roomService() - ?.getRoomSummariesLive(roomSummaryQueryParams()) - ?.map { list -> filterGalleries(list) } + fun getGalleriesFlow() = combine( + MatrixSessionProvider.getSessionOrThrow().roomService() + .getRoomSummariesLive(roomSummaryQueryParams()).asFlow(), + MatrixSessionProvider.getSessionOrThrow().roomService().getChangeMembershipsLive().asFlow() + ) { roomSummaries, _ -> + withContext(Dispatchers.IO) { filterGalleries(roomSummaries) } + }.distinctUntilChanged() + private fun filterGalleries(list: List<RoomSummary>): List<GalleryListItem> { - return list.mapNotNull { summary -> - if (summary.roomType == GALLERY_TYPE && summary.membership == Membership.JOIN) { - summary.toGalleryListItem() - } else null + val groups = list.filter { it.roomType == GALLERY_TYPE } + val joined = groups.mapNotNull { it.takeIf { it.membership == Membership.JOIN } } + val invites = groups.mapNotNull { it.takeIf { it.membership == Membership.INVITE } } + val knocks = getKnockRequestToJoinedGroups(joined) + return mutableListOf<GalleryListItem>().apply { + addAll(knocks) + addAll(invites.map { it.toInvitedGalleryListItem() }) + addAll(joined.map { it.toJoinedGalleryListItem() }) + } + } + + private fun getKnockRequestToJoinedGroups(joined: List<RoomSummary>): List<RequestGalleryListItem> { + val requests = mutableListOf<RequestGalleryListItem>() + + joined.forEach { groupSummary -> + val group = + MatrixSessionProvider.currentSession?.getRoom(groupSummary.roomId) ?: return@forEach + + val knockingMembers = + group.membershipService().getRoomMembers(roomMemberQueryParams { + memberships = listOf(Membership.KNOCK) + }).takeIf { it.isNotEmpty() } ?: return@forEach + + + knockingMembers.forEach { user -> + requests.add( + RequestGalleryListItem( + id = groupSummary.roomId, + info = groupSummary.toRoomInfo(), + requesterName = user.displayName + ?: UserUtils.removeDomainSuffix(user.userId), + requesterId = user.userId + ) + ) + } } + return requests } -} \ No newline at end of file +} diff --git a/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosFragment.kt b/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosFragment.kt index 172e1311d..0f1004782 100644 --- a/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosFragment.kt +++ b/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosFragment.kt @@ -20,11 +20,12 @@ import org.futo.circles.core.CirclesAppConfig import org.futo.circles.core.databinding.FragmentRoomsBinding import org.futo.circles.core.extensions.navigateSafe import org.futo.circles.core.extensions.observeData -import org.futo.circles.core.picker.helper.RuntimePermissionHelper -import org.futo.circles.gallery.R -import org.futo.circles.core.picker.gallery.rooms.list.PhotosListAdapter +import org.futo.circles.core.extensions.observeResponse import org.futo.circles.core.model.GalleryListItem +import org.futo.circles.core.model.RequestGalleryListItem +import org.futo.circles.core.picker.helper.RuntimePermissionHelper import org.futo.circles.core.view.EmptyTabPlaceholderView +import org.futo.circles.gallery.R @AndroidEntryPoint class PhotosFragment : Fragment(org.futo.circles.core.R.layout.fragment_rooms), MenuProvider { @@ -33,7 +34,14 @@ class PhotosFragment : Fragment(org.futo.circles.core.R.layout.fragment_rooms), private val binding by viewBinding(FragmentRoomsBinding::bind) private val listAdapter by lazy { - PhotosListAdapter(onRoomClicked = { roomListItem -> onRoomListItemClicked(roomListItem) }) + PhotosListAdapter( + onRoomClicked = { roomListItem -> onRoomListItemClicked(roomListItem) }, + onInviteClicked = { roomListItem, isAccepted -> + onInviteClicked(roomListItem, isAccepted) + }, + onRequestClicked = { roomListItem, isAccepted -> + onRequestClicked(roomListItem, isAccepted) + }) } @RequiresApi(Build.VERSION_CODES.TIRAMISU) @@ -75,7 +83,8 @@ class PhotosFragment : Fragment(org.futo.circles.core.R.layout.fragment_rooms), } private fun setupObservers() { - viewModel.roomsLiveData?.observeData(this) { listAdapter.submitList(it) } + viewModel.roomsLiveData.observeData(this) { listAdapter.submitList(it) } + viewModel.inviteResultLiveData.observeResponse(this) } private fun onRoomListItemClicked(room: GalleryListItem) { @@ -88,6 +97,16 @@ class PhotosFragment : Fragment(org.futo.circles.core.R.layout.fragment_rooms), else navigateToBackupSettings() } + private fun onInviteClicked(room: GalleryListItem, isAccepted: Boolean) { + if (isAccepted) viewModel.acceptPhotosInvite(room.id) + else viewModel.rejectInvite(room.id) + } + + private fun onRequestClicked(room: RequestGalleryListItem, isAccepted: Boolean) { + if (isAccepted) viewModel.inviteUser(room) + else viewModel.kickUser(room) + } + private fun navigateToCreateRoom() { findNavController().navigateSafe(PhotosFragmentDirections.toCreateGalleryDialogFragment()) } diff --git a/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosListAdapter.kt b/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosListAdapter.kt new file mode 100644 index 000000000..22423eabd --- /dev/null +++ b/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosListAdapter.kt @@ -0,0 +1,58 @@ +package org.futo.circles.gallery.feature + +import android.view.ViewGroup +import org.futo.circles.core.list.BaseRvAdapter +import org.futo.circles.core.model.GalleryListItem +import org.futo.circles.core.model.InvitedGalleryListItem +import org.futo.circles.core.model.JoinedGalleryListItem +import org.futo.circles.core.model.RequestGalleryListItem +import org.futo.circles.core.picker.gallery.rooms.list.GalleryViewHolder +import org.futo.circles.core.picker.gallery.rooms.list.InvitedGalleryViewHolder +import org.futo.circles.core.picker.gallery.rooms.list.JoinedGalleryViewHolder +import org.futo.circles.core.picker.gallery.rooms.list.RequestGalleryViewHolder + +private enum class GalleryListItemViewType { JoinedGallery, InvitedGallery, KnockRequest } + +class PhotosListAdapter( + private val onRoomClicked: (GalleryListItem) -> Unit, + private val onInviteClicked: (GalleryListItem, Boolean) -> Unit, + private val onRequestClicked: (RequestGalleryListItem, Boolean) -> Unit +) : BaseRvAdapter<GalleryListItem, GalleryViewHolder>(DefaultIdEntityCallback()) { + + override fun getItemViewType(position: Int): Int = when (getItem(position)) { + is JoinedGalleryListItem -> GalleryListItemViewType.JoinedGallery.ordinal + is InvitedGalleryListItem -> GalleryListItemViewType.InvitedGallery.ordinal + is RequestGalleryListItem -> GalleryListItemViewType.KnockRequest.ordinal + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ) = when (GalleryListItemViewType.values()[viewType]) { + GalleryListItemViewType.JoinedGallery -> JoinedGalleryViewHolder( + parent = parent, + onGalleryClicked = { position -> onRoomClicked(getItem(position)) } + ) + + GalleryListItemViewType.InvitedGallery -> InvitedGalleryViewHolder( + parent = parent, + onInviteClicked = { position, isAccepted -> + onInviteClicked(getItem(position), isAccepted) + } + ) + + GalleryListItemViewType.KnockRequest -> RequestGalleryViewHolder( + parent = parent, + onRequestClicked = { position, isAccepted -> + (getItem(position) as? RequestGalleryListItem)?.let { + onRequestClicked(it, isAccepted) + } + } + ) + } + + override fun onBindViewHolder(holder: GalleryViewHolder, position: Int) { + holder.bind(getItem(position)) + } + +} \ No newline at end of file diff --git a/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosViewModel.kt b/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosViewModel.kt index f9aa6e6a0..f04479d3d 100644 --- a/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosViewModel.kt +++ b/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosViewModel.kt @@ -1,14 +1,48 @@ package org.futo.circles.gallery.feature import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData import dagger.hilt.android.lifecycle.HiltViewModel +import org.futo.circles.core.SingleEventLiveData +import org.futo.circles.core.extensions.Response +import org.futo.circles.core.extensions.launchBg +import org.futo.circles.core.model.CircleRoomTypeArg +import org.futo.circles.core.model.RequestGalleryListItem +import org.futo.circles.core.room.invite.InviteRequestsDataSource import javax.inject.Inject @HiltViewModel class PhotosViewModel @Inject constructor( - dataSource: PhotosDataSource + dataSource: PhotosDataSource, + private val inviteRequestsDataSource: InviteRequestsDataSource ) : ViewModel() { - val roomsLiveData = dataSource.getGalleriesLiveData() + val roomsLiveData = dataSource.getGalleriesFlow().asLiveData() + val inviteResultLiveData = SingleEventLiveData<Response<Unit?>>() + + fun rejectInvite(roomId: String) { + launchBg { + val result = inviteRequestsDataSource.rejectInvite(roomId) + inviteResultLiveData.postValue(result) + } + } + + fun acceptPhotosInvite(roomId: String) { + launchBg { + val result = inviteRequestsDataSource.acceptInvite(roomId, CircleRoomTypeArg.Photo) + inviteResultLiveData.postValue(result) + } + } + + fun inviteUser(room: RequestGalleryListItem) { + launchBg { + val result = inviteRequestsDataSource.inviteUser(room.id, room.requesterId) + inviteResultLiveData.postValue(result) + } + } + + fun kickUser(room: RequestGalleryListItem) { + launchBg { inviteRequestsDataSource.kickUser(room.id, room.requesterId) } + } } \ No newline at end of file diff --git a/gallery/src/main/res/layout/list_item_select_gallery.xml b/gallery/src/main/res/layout/list_item_select_gallery.xml index f4bf56930..d77f23155 100644 --- a/gallery/src/main/res/layout/list_item_select_gallery.xml +++ b/gallery/src/main/res/layout/list_item_select_gallery.xml @@ -7,7 +7,7 @@ <include android:id="@+id/baseGalleryItem" - layout="@layout/list_item_gallery" + layout="@layout/list_item_joined_gallery" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" -- GitLab