From 62c5243ecd4c51580c419c7c9a4d4af154d1cd89 Mon Sep 17 00:00:00 2001 From: Liviu Timar <65943217+liviu-timar@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:31:27 +0300 Subject: [PATCH] Add ChatTheme property that controls message options visibility --- .../compose/sample/ui/MessagesActivity.kt | 2 + .../api/stream-chat-android-compose.api | 33 +++++++++++++- .../MessageOptionItemVisibility.kt | 45 +++++++++++++++++++ .../messageoptions/MessageOptions.kt | 22 +++++---- .../android/compose/ui/theme/ChatTheme.kt | 21 +++++++-- 5 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messageoptions/MessageOptionItemVisibility.kt diff --git a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt index f09393f9b08..71e87215d44 100644 --- a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt +++ b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt @@ -53,6 +53,7 @@ import io.getstream.chat.android.compose.sample.R import io.getstream.chat.android.compose.state.mediagallerypreview.MediaGalleryPreviewResultType import io.getstream.chat.android.compose.state.messages.attachments.StatefulStreamMediaRecorder import io.getstream.chat.android.compose.ui.components.composer.MessageInput +import io.getstream.chat.android.compose.ui.components.messageoptions.MessageOptionItemVisibility import io.getstream.chat.android.compose.ui.components.messageoptions.defaultMessageOptionsState import io.getstream.chat.android.compose.ui.components.reactionpicker.ReactionsPicker import io.getstream.chat.android.compose.ui.components.selectedmessage.SelectedMessageMenu @@ -114,6 +115,7 @@ class MessagesActivity : BaseConnectedActivity() { dateFormatter = ChatApp.dateFormatter, autoTranslationEnabled = ChatApp.autoTranslationEnabled, isComposerLinkPreviewEnabled = ChatApp.isComposerLinkPreviewEnabled, + messageOptionItemVisibility = MessageOptionItemVisibility(), allowUIAutomationTest = true, messageComposerTheme = MessageComposerTheme.defaultTheme(typography).let { messageComposerTheme -> messageComposerTheme.copy( diff --git a/stream-chat-android-compose/api/stream-chat-android-compose.api b/stream-chat-android-compose/api/stream-chat-android-compose.api index 773bb76631f..76045e8dab6 100644 --- a/stream-chat-android-compose/api/stream-chat-android-compose.api +++ b/stream-chat-android-compose/api/stream-chat-android-compose.api @@ -959,6 +959,36 @@ public final class io/getstream/chat/android/compose/ui/components/messageoption public static final fun MessageOptionItem (Lio/getstream/chat/android/compose/state/messageoptions/MessageOptionItemState;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Alignment$Vertical;Landroidx/compose/foundation/layout/Arrangement$Horizontal;Landroidx/compose/runtime/Composer;II)V } +public final class io/getstream/chat/android/compose/ui/components/messageoptions/MessageOptionItemVisibility { + public static final field $stable I + public fun ()V + public fun (ZZZZZZZZZ)V + public synthetic fun (ZZZZZZZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Z + public final fun component2 ()Z + public final fun component3 ()Z + public final fun component4 ()Z + public final fun component5 ()Z + public final fun component6 ()Z + public final fun component7 ()Z + public final fun component8 ()Z + public final fun component9 ()Z + public final fun copy (ZZZZZZZZZ)Lio/getstream/chat/android/compose/ui/components/messageoptions/MessageOptionItemVisibility; + public static synthetic fun copy$default (Lio/getstream/chat/android/compose/ui/components/messageoptions/MessageOptionItemVisibility;ZZZZZZZZZILjava/lang/Object;)Lio/getstream/chat/android/compose/ui/components/messageoptions/MessageOptionItemVisibility; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public final fun isCopyTextVisible ()Z + public final fun isDeleteMessageVisible ()Z + public final fun isEditMessageVisible ()Z + public final fun isFlagMessageVisible ()Z + public final fun isMarkAsUnreadVisible ()Z + public final fun isPinMessageVisible ()Z + public final fun isReplyVisible ()Z + public final fun isRetryMessageVisible ()Z + public final fun isThreadReplyVisible ()Z + public fun toString ()Ljava/lang/String; +} + public final class io/getstream/chat/android/compose/ui/components/messageoptions/MessageOptionsKt { public static final fun MessageOptions (Ljava/util/List;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V public static final fun defaultMessageOptionsState (Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;ZLjava/util/Set;Landroidx/compose/runtime/Composer;I)Ljava/util/List; @@ -1481,6 +1511,7 @@ public final class io/getstream/chat/android/compose/ui/theme/ChatTheme { public final fun getMessageAlignmentProvider (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/util/MessageAlignmentProvider; public final fun getMessageComposerTheme (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/theme/MessageComposerTheme; public final fun getMessageDateSeparatorTheme (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/theme/MessageDateSeparatorTheme; + public final fun getMessageOptionItemVisibility (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/components/messageoptions/MessageOptionItemVisibility; public final fun getMessageOptionsUserReactionAlignment (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/ui/common/state/messages/list/MessageOptionsUserReactionAlignment; public final fun getMessagePreviewFormatter (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/util/MessagePreviewFormatter; public final fun getMessageTextFormatter (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/util/MessageTextFormatter; @@ -1501,7 +1532,7 @@ public final class io/getstream/chat/android/compose/ui/theme/ChatTheme { } public final class io/getstream/chat/android/compose/ui/theme/ChatThemeKt { - public static final fun ChatTheme (ZZZLio/getstream/chat/android/compose/ui/theme/StreamColors;Lio/getstream/chat/android/compose/ui/theme/StreamDimens;Lio/getstream/chat/android/compose/ui/theme/StreamTypography;Lio/getstream/chat/android/compose/ui/theme/StreamShapes;Landroidx/compose/material/ripple/RippleTheme;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lio/getstream/chat/android/compose/ui/util/ReactionIconFactory;ZLio/getstream/chat/android/ui/common/helper/DateFormatter;Lio/getstream/chat/android/ui/common/utils/ChannelNameFormatter;Lio/getstream/chat/android/compose/ui/util/MessagePreviewFormatter;Lio/getstream/chat/android/compose/ui/util/SearchResultNameFormatter;Lio/getstream/chat/android/compose/ui/util/StreamCoilImageLoaderFactory;Lio/getstream/chat/android/compose/ui/util/MessageAlignmentProvider;Lio/getstream/chat/android/ui/common/state/messages/list/MessageOptionsUserReactionAlignment;Ljava/util/List;ZLio/getstream/chat/android/ui/common/images/resizing/StreamCdnImageResizing;ZLio/getstream/chat/android/compose/ui/theme/MessageTheme;Lio/getstream/chat/android/compose/ui/theme/MessageTheme;Lio/getstream/chat/android/compose/ui/theme/MessageDateSeparatorTheme;Lio/getstream/chat/android/compose/ui/theme/MessageUnreadSeparatorTheme;Lio/getstream/chat/android/compose/ui/theme/MessageComposerTheme;Lio/getstream/chat/android/compose/ui/util/MessageTextFormatter;Lio/getstream/chat/android/compose/ui/util/QuotedMessageTextFormatter;Lio/getstream/sdk/chat/audio/recording/StreamMediaRecorder;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;IIIIII)V + public static final fun ChatTheme (ZZZLio/getstream/chat/android/compose/ui/theme/StreamColors;Lio/getstream/chat/android/compose/ui/theme/StreamDimens;Lio/getstream/chat/android/compose/ui/theme/StreamTypography;Lio/getstream/chat/android/compose/ui/theme/StreamShapes;Landroidx/compose/material/ripple/RippleTheme;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lio/getstream/chat/android/compose/ui/util/ReactionIconFactory;ZLio/getstream/chat/android/ui/common/helper/DateFormatter;Lio/getstream/chat/android/ui/common/utils/ChannelNameFormatter;Lio/getstream/chat/android/compose/ui/util/MessagePreviewFormatter;Lio/getstream/chat/android/compose/ui/util/SearchResultNameFormatter;Lio/getstream/chat/android/compose/ui/util/StreamCoilImageLoaderFactory;Lio/getstream/chat/android/compose/ui/util/MessageAlignmentProvider;Lio/getstream/chat/android/compose/ui/components/messageoptions/MessageOptionItemVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageOptionsUserReactionAlignment;Ljava/util/List;ZLio/getstream/chat/android/ui/common/images/resizing/StreamCdnImageResizing;ZLio/getstream/chat/android/compose/ui/theme/MessageTheme;Lio/getstream/chat/android/compose/ui/theme/MessageTheme;Lio/getstream/chat/android/compose/ui/theme/MessageDateSeparatorTheme;Lio/getstream/chat/android/compose/ui/theme/MessageUnreadSeparatorTheme;Lio/getstream/chat/android/compose/ui/theme/MessageComposerTheme;Lio/getstream/chat/android/compose/ui/util/MessageTextFormatter;Lio/getstream/chat/android/compose/ui/util/QuotedMessageTextFormatter;Lio/getstream/sdk/chat/audio/recording/StreamMediaRecorder;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;IIIIII)V } public final class io/getstream/chat/android/compose/ui/theme/ComponentSize { diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messageoptions/MessageOptionItemVisibility.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messageoptions/MessageOptionItemVisibility.kt new file mode 100644 index 00000000000..7d2132ffd26 --- /dev/null +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messageoptions/MessageOptionItemVisibility.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014-2024 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.compose.ui.components.messageoptions + +/** + * Controls the visibility of items in the message options menu. All options are visible by default. + * + * @param isRetryMessageVisible Visibility of the retry failed message option. + * @param isReplyVisible Visibility of the reply to message option (quote message). + * @param isThreadReplyVisible Visibility of the reply to message in thread option. + * @param isMarkAsUnreadVisible Visibility of the mark message as unread option. + * @param isCopyTextVisible Visibility of the copy message text option. + * @param isEditMessageVisible Visibility of the edit message option. + * @param isFlagMessageVisible Visibility of the flag message option. + * @param isPinMessageVisible Visibility of the pin message to chat option. + * @param isDeleteMessageVisible Visibility of the delete message option. + * + * @see [MessageOptions] + * @see [defaultMessageOptionsState] + */ +public data class MessageOptionItemVisibility( + val isRetryMessageVisible: Boolean = true, + val isReplyVisible: Boolean = true, + val isThreadReplyVisible: Boolean = true, + val isMarkAsUnreadVisible: Boolean = true, + val isCopyTextVisible: Boolean = true, + val isEditMessageVisible: Boolean = true, + val isFlagMessageVisible: Boolean = true, + val isPinMessageVisible: Boolean = true, + val isDeleteMessageVisible: Boolean = true, +) diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messageoptions/MessageOptions.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messageoptions/MessageOptions.kt index a8c8f28b8a3..f5d04f5d056 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messageoptions/MessageOptions.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messageoptions/MessageOptions.kt @@ -146,8 +146,11 @@ public fun defaultMessageOptionsState( val canMarkAsUnread = ownCapabilities.contains(ChannelCapabilities.READ_EVENTS) val canFlagMessage = ownCapabilities.contains(ChannelCapabilities.FLAG_MESSAGE) + // options menu item visibility + val visibility = ChatTheme.messageOptionItemVisibility + return listOfNotNull( - if (isOwnMessage && isMessageFailed) { + if (visibility.isRetryMessageVisible && isOwnMessage && isMessageFailed) { MessageOptionItemState( title = R.string.stream_compose_resend_message, iconPainter = painterResource(R.drawable.stream_compose_ic_resend), @@ -158,7 +161,7 @@ public fun defaultMessageOptionsState( } else { null }, - if (isMessageSynced && canQuoteMessage) { + if (visibility.isReplyVisible && isMessageSynced && canQuoteMessage) { MessageOptionItemState( title = R.string.stream_compose_reply, iconPainter = painterResource(R.drawable.stream_compose_ic_reply), @@ -169,7 +172,7 @@ public fun defaultMessageOptionsState( } else { null }, - if (!isInThread && isMessageSynced && canThreadReply) { + if (visibility.isThreadReplyVisible && !isInThread && isMessageSynced && canThreadReply) { MessageOptionItemState( title = R.string.stream_compose_thread_reply, iconPainter = painterResource(R.drawable.stream_compose_ic_thread), @@ -180,7 +183,7 @@ public fun defaultMessageOptionsState( } else { null }, - if (canMarkAsUnread) { + if (visibility.isMarkAsUnreadVisible && canMarkAsUnread) { MessageOptionItemState( title = R.string.stream_compose_mark_as_unread, iconPainter = painterResource(R.drawable.stream_compose_ic_mark_as_unread), @@ -191,7 +194,7 @@ public fun defaultMessageOptionsState( } else { null }, - if (isTextOnlyMessage || hasLinks) { + if (visibility.isCopyTextVisible && (isTextOnlyMessage || hasLinks)) { MessageOptionItemState( title = R.string.stream_compose_copy_message, iconPainter = painterResource(R.drawable.stream_compose_ic_copy), @@ -202,7 +205,8 @@ public fun defaultMessageOptionsState( } else { null }, - if (((isOwnMessage && canEditOwnMessage) || canEditAnyMessage) && !selectedMessage.isGiphy()) { + if (visibility.isEditMessageVisible && + (((isOwnMessage && canEditOwnMessage) || canEditAnyMessage) && !selectedMessage.isGiphy())) { MessageOptionItemState( title = R.string.stream_compose_edit_message, iconPainter = painterResource(R.drawable.stream_compose_ic_edit), @@ -213,7 +217,7 @@ public fun defaultMessageOptionsState( } else { null }, - if (canFlagMessage && !isOwnMessage) { + if (visibility.isFlagMessageVisible && canFlagMessage && !isOwnMessage) { MessageOptionItemState( title = R.string.stream_compose_flag_message, iconPainter = painterResource(R.drawable.stream_compose_ic_flag), @@ -224,7 +228,7 @@ public fun defaultMessageOptionsState( } else { null }, - if (isMessageSynced && canPinMessage) { + if (visibility.isPinMessageVisible && isMessageSynced && canPinMessage) { MessageOptionItemState( title = if (selectedMessage.pinned) R.string.stream_compose_unpin_message else R.string.stream_compose_pin_message, action = Pin(selectedMessage), @@ -235,7 +239,7 @@ public fun defaultMessageOptionsState( } else { null }, - if (canDeleteAnyMessage || (isOwnMessage && canDeleteOwnMessage)) { + if (visibility.isDeleteMessageVisible && (canDeleteAnyMessage || (isOwnMessage && canDeleteOwnMessage))) { MessageOptionItemState( title = R.string.stream_compose_delete_message, iconPainter = painterResource(R.drawable.stream_compose_ic_delete), diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt index e266f29580f..027a10a856a 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt @@ -39,6 +39,7 @@ import io.getstream.chat.android.client.header.VersionPrefixHeader import io.getstream.chat.android.compose.ui.attachments.AttachmentFactory import io.getstream.chat.android.compose.ui.attachments.StreamAttachmentFactories import io.getstream.chat.android.compose.ui.attachments.preview.handler.AttachmentPreviewHandler +import io.getstream.chat.android.compose.ui.components.messageoptions.MessageOptionItemVisibility import io.getstream.chat.android.compose.ui.messages.attachments.factory.AttachmentsPickerTabFactories import io.getstream.chat.android.compose.ui.messages.attachments.factory.AttachmentsPickerTabFactory import io.getstream.chat.android.compose.ui.util.LocalStreamImageLoader @@ -104,6 +105,9 @@ private val LocalSearchResultNameFormatter = compositionLocalOf { error("No MessageAlignmentProvider provided! Make sure to wrap all usages of Stream components in a ChatTheme.") } +private val LocalMessageOptionItemVisibility = compositionLocalOf { + error("No MessageOptionItemVisibility provided! Make sure to wrap all usages of Stream components in a ChatTheme.") +} private val LocalMessageOptionsUserReactionAlignment = compositionLocalOf { error( "No LocalMessageOptionsUserReactionAlignment provided! Make sure to wrap all usages of Stream components " + @@ -181,11 +185,13 @@ private val LocalStreamMediaRecorder = compositionLocalOf { * @param quotedAttachmentFactories Quoted attachment factories that we provide. * @param reactionIconFactory Used to create an icon [Painter] for the given reaction type. * @param allowUIAutomationTest Allow to simulate ui automation with given test tags. - * @param dateFormatter [DateFormatter] used throughout the app for date and time information. - * @param channelNameFormatter [ChannelNameFormatter] used throughout the app for channel names. - * @param messagePreviewFormatter [MessagePreviewFormatter] used to generate a string preview for the given message. + * @param dateFormatter [DateFormatter] Used throughout the app for date and time information. + * @param channelNameFormatter [ChannelNameFormatter] Used throughout the app for channel names. + * @param messagePreviewFormatter [MessagePreviewFormatter] Used to generate a string preview for the given message. * @param imageLoaderFactory A factory that creates new Coil [ImageLoader] instances. - * @param messageAlignmentProvider [MessageAlignmentProvider] used to provide message alignment for the given message. + * @param messageAlignmentProvider [MessageAlignmentProvider] Used to provide message alignment for the given message. + * @param messageOptionItemVisibility [MessageOptionItemVisibility] Determines menu item visibility in the + * message options menu. All options are visible by default. * @param messageOptionsUserReactionAlignment Alignment of the user reaction inside the message options. * @param attachmentsPickerTabFactories Attachments picker tab factories that we provide. * @param videoThumbnailsEnabled Dictates whether video thumbnails will be displayed inside video previews. @@ -227,6 +233,7 @@ public fun ChatTheme( searchResultNameFormatter: SearchResultNameFormatter = SearchResultNameFormatter.defaultFormatter(), imageLoaderFactory: StreamCoilImageLoaderFactory = StreamCoilImageLoaderFactory.defaultFactory(), messageAlignmentProvider: MessageAlignmentProvider = MessageAlignmentProvider.defaultMessageAlignmentProvider(), + messageOptionItemVisibility: MessageOptionItemVisibility = MessageOptionItemVisibility(), messageOptionsUserReactionAlignment: MessageOptionsUserReactionAlignment = MessageOptionsUserReactionAlignment.END, attachmentsPickerTabFactories: List = AttachmentsPickerTabFactories.defaultFactories(), videoThumbnailsEnabled: Boolean = true, @@ -298,6 +305,7 @@ public fun ChatTheme( LocalMessageComposerTheme provides messageComposerTheme, LocalStreamImageLoader provides imageLoaderFactory.imageLoader(LocalContext.current.applicationContext), LocalMessageAlignmentProvider provides messageAlignmentProvider, + LocalMessageOptionItemVisibility provides messageOptionItemVisibility, LocalMessageOptionsUserReactionAlignment provides messageOptionsUserReactionAlignment, LocalAttachmentsPickerTabFactories provides attachmentsPickerTabFactories, LocalVideoThumbnailsEnabled provides videoThumbnailsEnabled, @@ -444,6 +452,11 @@ public object ChatTheme { @ReadOnlyComposable get() = LocalMessageAlignmentProvider.current + public val messageOptionItemVisibility: MessageOptionItemVisibility + @Composable + @ReadOnlyComposable + get() = LocalMessageOptionItemVisibility.current + /** * Retrieves the current [MessageOptionsUserReactionAlignment] at the call site's position in the hierarchy. */