Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[i92] control options visibility for each channel #4869

Merged
merged 2 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
### ✅ Added
- Added Typing Users list to `ChannelItem`. [#4867](https://github.com/GetStream/stream-chat-android/pull/4867)
- Added typing indicator on `ChannelLitsView`. [#4867](https://github.com/GetStream/stream-chat-android/pull/4867)
- Added options visibility customization for each channel in a list. [#4869](https://github.com/GetStream/stream-chat-android/pull/4869)

### ⚠️ Changed
- Create new `bind()` method on `BaseChannelListItemViewHolder` that takes as parameter `ChannelItem`. [#4867](https://github.com/GetStream/stream-chat-android/pull/4867)
Expand Down
27 changes: 0 additions & 27 deletions stream-chat-android-client/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,12 @@
<ID>ComplexMethod:Attachment.kt$Attachment$override fun toString(): String</ID>
<ID>ComplexMethod:Message.kt$Message$override fun toString(): String</ID>
<ID>ComplexMethod:User.kt$User$override fun getComparableField(fieldName: String): Comparable&lt;*>?</ID>
<ID>FinalNewline:io.getstream.chat.android.client.chatclient.WhenFetchCurrentUser.kt:1</ID>
<ID>FinalNewline:io.getstream.chat.android.client.user.CurrentUserFetcher.kt:1</ID>
<ID>FinalNewline:io.getstream.chat.android.client.user.CurrentUserFetcherTests.kt:1</ID>
<ID>FinalNewline:io.getstream.chat.android.client.user.CurrentUserUrlBuilderTests.kt:1</ID>
<ID>ForbiddenComment:MessageUtils.kt$// TODO: type should be a sealed/class or enum at the client level</ID>
<ID>LongParameterList:BaseChatModule.kt$BaseChatModule$( private val appContext: Context, private val clientScope: ClientScope, private val userScope: UserScope, private val config: ChatClientConfig, private val notificationsHandler: NotificationHandler, private val notificationConfig: NotificationConfig, private val fileUploader: FileUploader? = null, private val tokenManager: TokenManager = TokenManagerImpl(), private val customOkHttpClient: OkHttpClient? = null, private val lifecycle: Lifecycle, private val httpClientConfig: (OkHttpClient.Builder) -> OkHttpClient.Builder = { it }, )</ID>
<ID>MagicNumber:Attachment.kt$Attachment$9</ID>
<ID>MaxLineLength:Message.kt$*</ID>
<ID>MaxLineLength:Reaction.kt$*</ID>
<ID>NewLineAtEndOfFile:CurrentUserFetcher.kt$io.getstream.chat.android.client.user.CurrentUserFetcher.kt</ID>
<ID>NewLineAtEndOfFile:CurrentUserFetcherTests.kt$io.getstream.chat.android.client.user.CurrentUserFetcherTests.kt</ID>
<ID>NewLineAtEndOfFile:CurrentUserUrlBuilderTests.kt$io.getstream.chat.android.client.user.CurrentUserUrlBuilderTests.kt</ID>
<ID>NewLineAtEndOfFile:WhenFetchCurrentUser.kt$io.getstream.chat.android.client.chatclient.WhenFetchCurrentUser.kt</ID>
<ID>NoBlankLineBeforeRbrace:io.getstream.chat.android.client.user.CurrentUserFetcher.kt:97</ID>
<ID>NoConsecutiveBlankLines:io.getstream.chat.android.client.chatclient.WhenFetchCurrentUser.kt:130</ID>
<ID>NoUnusedImports:io.getstream.chat.android.client.DevicesApiCallsTests.kt:26</ID>
<ID>NoUnusedImports:io.getstream.chat.android.client.chatclient.WhenFetchCurrentUser.kt:14</ID>
<ID>NoUnusedImports:io.getstream.chat.android.client.chatclient.WhenFetchCurrentUser.kt:25</ID>
<ID>NoUnusedImports:io.getstream.chat.android.client.chatclient.WhenFetchCurrentUser.kt:5</ID>
<ID>NoUnusedImports:io.getstream.chat.android.client.chatclient.WhenFetchCurrentUser.kt:6</ID>
<ID>NoUnusedImports:io.getstream.chat.android.client.chatclient.WhenFetchCurrentUser.kt:9</ID>
<ID>NoUnusedImports:io.getstream.chat.android.client.user.CurrentUserFetcher.kt:44</ID>
<ID>ReturnCount:Channel.kt$@InternalStreamChatApi public fun Channel.addMember(member: Member?): Channel</ID>
<ID>ReturnCount:CurrentUserFetcher.kt$CurrentUserFetcherImpl$override suspend fun fetch(): Result&lt;User></ID>
<ID>TooGenericExceptionCaught:CurrentUserFetcher.kt$CurrentUserFetcherImpl$e: Throwable</ID>
<ID>TooGenericExceptionCaught:CurrentUserFetcher.kt$CurrentUserFetcherImpl$t: Throwable</ID>
<ID>UnusedImports:CurrentUserFetcher.kt$import java.net.URI</ID>
<ID>UnusedImports:DevicesApiCallsTests.kt$import kotlinx.coroutines.ExperimentalCoroutinesApi</ID>
<ID>UnusedImports:WhenFetchCurrentUser.kt$import io.getstream.chat.android.client.api.models.QueryChannelRequest</ID>
<ID>UnusedImports:WhenFetchCurrentUser.kt$import io.getstream.chat.android.client.call.CoroutineCall</ID>
<ID>UnusedImports:WhenFetchCurrentUser.kt$import io.getstream.chat.android.client.errors.ChatError</ID>
<ID>UnusedImports:WhenFetchCurrentUser.kt$import io.getstream.chat.android.client.plugin.listeners.QueryChannelListener</ID>
<ID>UnusedImports:WhenFetchCurrentUser.kt$import org.mockito.kotlin.doAnswer</ID>
<ID>UnusedPrivateMember:BaseChatModule.kt$BaseChatModule$private val clientScope: ClientScope</ID>
<ID>UnusedPrivateMember:ChatEventsObservable.kt$ChatEventsObservable$private val logger = StreamLog.getLogger("Chat:EventsObservable")</ID>
</CurrentIssues>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,8 @@ public final class io/getstream/chat/android/ui/channel/list/ChannelListView : a
public final fun setEmptyStateView (Landroid/view/View;Landroid/widget/FrameLayout$LayoutParams;)V
public static synthetic fun setEmptyStateView$default (Lio/getstream/chat/android/ui/channel/list/ChannelListView;Landroid/view/View;Landroid/widget/FrameLayout$LayoutParams;ILjava/lang/Object;)V
public final fun setErrorEventHandler (Lio/getstream/chat/android/ui/channel/list/ChannelListView$ErrorEventHandler;)V
public final fun setIsDeleteOptionVisible (Lkotlin/jvm/functions/Function1;)V
public final fun setIsMoreOptionsVisible (Lkotlin/jvm/functions/Function1;)V
public final fun setItemSeparator (I)V
public final fun setItemSeparatorHeight (I)V
public final fun setLoadingView (Landroid/view/View;)V
Expand Down Expand Up @@ -1027,6 +1029,15 @@ public abstract interface class io/getstream/chat/android/ui/channel/list/Channe
public final class io/getstream/chat/android/ui/channel/list/ChannelListView$ChannelLongClickListener$Companion {
}

public abstract interface class io/getstream/chat/android/ui/channel/list/ChannelListView$ChannelOptionVisibilityPredicate : kotlin/jvm/functions/Function1 {
public static final field Companion Lio/getstream/chat/android/ui/channel/list/ChannelListView$ChannelOptionVisibilityPredicate$Companion;
public static final field DEFAULT Lio/getstream/chat/android/ui/channel/list/ChannelListView$ChannelOptionVisibilityPredicate;
public abstract fun invoke (Lio/getstream/chat/android/client/models/Channel;)Ljava/lang/Boolean;
}

public final class io/getstream/chat/android/ui/channel/list/ChannelListView$ChannelOptionVisibilityPredicate$Companion {
}

public abstract interface class io/getstream/chat/android/ui/channel/list/ChannelListView$EndReachedListener {
public abstract fun onEndReached ()V
}
Expand Down Expand Up @@ -1185,6 +1196,7 @@ public class io/getstream/chat/android/ui/channel/list/adapter/viewholder/Channe
public fun getItemViewType (Lio/getstream/chat/android/ui/channel/list/adapter/ChannelListItem;)I
protected final fun getListenerContainer ()Lio/getstream/chat/android/ui/channel/list/adapter/viewholder/ChannelListListenerContainer;
protected final fun getStyle ()Lio/getstream/chat/android/ui/channel/list/ChannelListViewStyle;
protected final fun getVisibilityContainer ()Lio/getstream/chat/android/ui/channel/list/adapter/viewholder/ChannelListVisibilityContainer;
}

public abstract interface class io/getstream/chat/android/ui/channel/list/adapter/viewholder/ChannelListListenerContainer {
Expand All @@ -1196,6 +1208,11 @@ public abstract interface class io/getstream/chat/android/ui/channel/list/adapte
public abstract fun getUserClickListener ()Lio/getstream/chat/android/ui/channel/list/ChannelListView$UserClickListener;
}

public abstract interface class io/getstream/chat/android/ui/channel/list/adapter/viewholder/ChannelListVisibilityContainer {
public abstract fun isDeleteOptionVisible ()Lio/getstream/chat/android/ui/channel/list/ChannelListView$ChannelOptionVisibilityPredicate;
public abstract fun isMoreOptionsVisible ()Lio/getstream/chat/android/ui/channel/list/ChannelListView$ChannelOptionVisibilityPredicate;
}

public abstract class io/getstream/chat/android/ui/channel/list/adapter/viewholder/SwipeViewHolder : io/getstream/chat/android/ui/channel/list/adapter/viewholder/BaseChannelListItemViewHolder {
public fun <init> (Landroid/view/View;)V
public abstract fun getClosedX ()F
Expand Down
2 changes: 1 addition & 1 deletion stream-chat-android-ui-components/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@
<ID>MaxLineLength:AutoLinkableTextTransformer.kt$AutoLinkableTextTransformer$*</ID>
<ID>MaxLineLength:AvatarView.kt$AvatarView$*</ID>
<ID>MaxLineLength:ChannelActionsDialogViewStyle.kt$ChannelActionsDialogViewStyle$*</ID>
<ID>MaxLineLength:ChannelListPayloadDiff.kt$ChannelListPayloadDiff$return nameChanged || avatarViewChanged || usersChanged || lastMessageChanged || readStateChanged || unreadCountChanged || extraDataChanged</ID>
<ID>MaxLineLength:ChannelListView.kt$ChannelListView$is ChannelListViewModel.ErrorEvent.DeleteChannelError -> R.string.stream_ui_channel_list_error_delete_channel</ID>
<ID>MaxLineLength:ChannelListViewModel.kt$ChannelListViewModel$*</ID>
<ID>MaxLineLength:ChannelListViewModelFactory.kt$ChannelListViewModelFactory$*</ID>
<ID>MaxLineLength:ChannelListViewStyle.kt$ChannelListViewStyle$*</ID>
<ID>MaxLineLength:ChannelListVisibilityContainerImpl.kt$ChannelListVisibilityContainerImpl$override</ID>
<ID>MaxLineLength:ChatUI.kt$ChatUI$public</ID>
<ID>MaxLineLength:Context.kt$theme.resolveAttribute(R.attr.streamUiTheme, typedValue, true) -> ContextThemeWrapper(this, typedValue.resourceId)</ID>
<ID>MaxLineLength:DefaultMessageComposerLeadingContent.kt$DefaultMessageComposerLeadingContent$binding.attachmentsButton.isVisible = style.attachmentsButtonVisible &amp;&amp; canSendMessage &amp;&amp; canUploadFile &amp;&amp; !isInEditMode</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,24 @@ public class ChannelListView : FrameLayout {
simpleChannelListView.setMoreOptionsClickListener(listener)
}

/**
* Allows clients to set a visibility controller for the "more options" icon in ViewHolder items.
*
* @param isMoreOptionsVisible The callback to be invoked when the visibility of "more options" gets checked.
*/
public fun setIsMoreOptionsVisible(isMoreOptionsVisible: (Channel) -> Boolean) {
simpleChannelListView.setIsMoreOptionsVisible(isMoreOptionsVisible)
}

/**
* Allows clients to set a visibility controller for the "delete option" icon in ViewHolder items.
*
* @param isDeleteOptionVisible The callback to be invoked when the visibility of "delete option" gets checked.
*/
public fun setIsDeleteOptionVisible(isDeleteOptionVisible: (Channel) -> Boolean) {
simpleChannelListView.setIsDeleteOptionVisible(isDeleteOptionVisible)
}

/**
* Allows a client to set a click listener to be notified of "channel info" clicks in the "more options" menu.
*
Expand Down Expand Up @@ -501,6 +519,23 @@ public class ChannelListView : FrameLayout {
public fun onLongClick(channel: Channel): Boolean
}

public fun interface ChannelOptionVisibilityPredicate : Function1<Channel, Boolean> {
public companion object {
@JvmField
public val DEFAULT: ChannelOptionVisibilityPredicate = ChannelOptionVisibilityPredicate {
// option is visible by default
true
}
}

/**
* Called to check option's visibility for the specified [channel].
*
* @return True if the option is visible.
*/
override fun invoke(p1: Channel): Boolean
}

public fun interface EndReachedListener {
public fun onEndReached()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,20 @@ public open class ChannelListItemViewHolderFactory {
protected lateinit var listenerContainer: ChannelListListenerContainer
private set

protected lateinit var visibilityContainer: ChannelListVisibilityContainer
private set

protected lateinit var style: ChannelListViewStyle
private set

internal fun setListenerContainer(listenerContainer: ChannelListListenerContainer) {
this.listenerContainer = listenerContainer
}

internal fun setVisibilityContainer(visibilityContainer: ChannelListVisibilityContainer) {
this.visibilityContainer = visibilityContainer
}

internal fun setStyle(style: ChannelListViewStyle) {
this.style = style
}
Expand Down Expand Up @@ -80,6 +87,8 @@ public open class ChannelListItemViewHolderFactory {
listenerContainer.userClickListener,
listenerContainer.swipeListener,
style,
visibilityContainer.isMoreOptionsVisible,
visibilityContainer.isDeleteOptionVisible,
)
}

Expand All @@ -94,6 +103,9 @@ public open class ChannelListItemViewHolderFactory {
if (!::listenerContainer.isInitialized) {
listenerContainer = ChannelListListenerContainerImpl()
}
if (!::visibilityContainer.isInitialized) {
visibilityContainer = ChannelListVisibilityContainerImpl()
}
if (!::style.isInitialized) {
style = ChannelListViewStyle(context, null)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2014-2022 Stream.io Inc. All rights reserved.
*
* Licensed under the Stream License;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
*
* 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 io.getstream.chat.android.ui.channel.list.adapter.viewholder

import io.getstream.chat.android.ui.channel.list.ChannelListView.ChannelOptionVisibilityPredicate

public sealed interface ChannelListVisibilityContainer {
public val isMoreOptionsVisible: ChannelOptionVisibilityPredicate
public val isDeleteOptionVisible: ChannelOptionVisibilityPredicate
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2014-2023 Stream.io Inc. All rights reserved.
*
* Licensed under the Stream License;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
*
* 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 io.getstream.chat.android.ui.channel.list.adapter.viewholder

import com.getstream.sdk.chat.utils.ListenerDelegate
import io.getstream.chat.android.ui.channel.list.ChannelListView.ChannelOptionVisibilityPredicate

internal class ChannelListVisibilityContainerImpl(
isMoreOptionsVisible: ChannelOptionVisibilityPredicate = ChannelOptionVisibilityPredicate.DEFAULT,
isDeleteOptionVisible: ChannelOptionVisibilityPredicate = ChannelOptionVisibilityPredicate.DEFAULT,
) : ChannelListVisibilityContainer {

override var isMoreOptionsVisible: ChannelOptionVisibilityPredicate by ListenerDelegate(isMoreOptionsVisible) { realPredicate ->
ChannelOptionVisibilityPredicate { channel ->
realPredicate().invoke(channel)
}
}

override var isDeleteOptionVisible: ChannelOptionVisibilityPredicate by ListenerDelegate(isDeleteOptionVisible) { realPredicate ->
ChannelOptionVisibilityPredicate { channel ->
realPredicate().invoke(channel)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ internal class ChannelViewHolder @JvmOverloads constructor(
private val userClickListener: ChannelListView.UserClickListener,
private val swipeListener: ChannelListView.SwipeListener,
private val style: ChannelListViewStyle,
private val isMoreOptionsVisible: ChannelListView.ChannelOptionVisibilityPredicate,
private val isDeleteOptionsVisible: ChannelListView.ChannelOptionVisibilityPredicate,
private val binding: StreamUiChannelListItemViewBinding = StreamUiChannelListItemViewBinding.inflate(
parent.streamThemeInflater,
parent,
Expand Down Expand Up @@ -184,7 +186,7 @@ internal class ChannelViewHolder @JvmOverloads constructor(
var optionsCount = 0

binding.itemBackgroundView.moreOptionsImageView.apply {
if (style.optionsEnabled) {
if (style.optionsEnabled && isMoreOptionsVisible(channel)) {
isVisible = true
optionsCount++
} else {
Expand All @@ -193,7 +195,7 @@ internal class ChannelViewHolder @JvmOverloads constructor(
}
binding.itemBackgroundView.deleteImageView.apply {
val canDeleteChannel = channel.ownCapabilities.contains(ChannelCapabilities.DELETE_CHANNEL)
if (canDeleteChannel && style.deleteEnabled) {
if (style.deleteEnabled && canDeleteChannel && isDeleteOptionsVisible(channel)) {
isVisible = true
optionsCount++
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import io.getstream.chat.android.ui.channel.list.adapter.ChannelListItem
import io.getstream.chat.android.ui.channel.list.adapter.internal.ChannelListItemAdapter
import io.getstream.chat.android.ui.channel.list.adapter.viewholder.ChannelListItemViewHolderFactory
import io.getstream.chat.android.ui.channel.list.adapter.viewholder.ChannelListListenerContainerImpl
import io.getstream.chat.android.ui.channel.list.adapter.viewholder.ChannelListVisibilityContainerImpl
import io.getstream.chat.android.ui.channel.list.adapter.viewholder.internal.ChannelItemSwipeListener
import io.getstream.chat.android.ui.common.extensions.internal.cast
import io.getstream.chat.android.ui.common.extensions.internal.getDrawableCompat
Expand All @@ -54,6 +55,8 @@ internal class SimpleChannelListView @JvmOverloads constructor(

internal val listenerContainer = ChannelListListenerContainerImpl()

internal val visibilityContainer = ChannelListVisibilityContainerImpl()

private lateinit var style: ChannelListViewStyle

init {
Expand Down Expand Up @@ -96,6 +99,7 @@ internal class SimpleChannelListView @JvmOverloads constructor(
}

viewHolderFactory.setListenerContainer(this.listenerContainer)
viewHolderFactory.setVisibilityContainer(this.visibilityContainer)
viewHolderFactory.setStyle(style)

adapter = ChannelListItemAdapter(viewHolderFactory)
Expand Down Expand Up @@ -134,6 +138,14 @@ internal class SimpleChannelListView @JvmOverloads constructor(
listenerContainer.moreOptionsClickListener = listener ?: ChannelListView.ChannelClickListener.DEFAULT
}

fun setIsMoreOptionsVisible(isMoreOptionsVisible: ChannelListView.ChannelOptionVisibilityPredicate) {
visibilityContainer.isMoreOptionsVisible = isMoreOptionsVisible
}

fun setIsDeleteOptionVisible(isDeleteOptionVisible: ChannelListView.ChannelOptionVisibilityPredicate) {
visibilityContainer.isDeleteOptionVisible = isDeleteOptionVisible
}

fun setSwipeListener(listener: ChannelListView.SwipeListener?) {
listenerContainer.swipeListener = listener ?: ChannelListView.SwipeListener.DEFAULT
}
Expand Down
Loading