diff --git a/build.gradle b/build.gradle index 249e680f..6c8f001b 100644 --- a/build.gradle +++ b/build.gradle @@ -9,6 +9,7 @@ buildscript { dependencies { classpath libs.androidGradlePlugin classpath libs.kotlinGradlePlugin + classpath libs.composeCompilerGradlePlugin classpath libs.navigationSafeArgsGradlePlugin classpath libs.spotlessPlugin } diff --git a/compose-custom-attachments/build.gradle b/compose-custom-attachments/build.gradle index af502b43..210244c0 100644 --- a/compose-custom-attachments/build.gradle +++ b/compose-custom-attachments/build.gradle @@ -1,10 +1,11 @@ plugins { id 'com.android.application' id 'kotlin-android' + id 'org.jetbrains.kotlin.plugin.compose' } android { - compileSdk 34 + compileSdk 36 defaultConfig { applicationId "io.getstream.chat.android.compose.customattachments" @@ -24,9 +25,6 @@ android { buildFeatures { compose true } - composeOptions { - kotlinCompilerExtensionVersion libs.versions.composeCompiler.get() - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -35,16 +33,13 @@ android { dependencies { // Stream SDK - implementation "io.getstream:stream-chat-android-compose:6.1.1" - implementation "io.getstream:stream-chat-android-state:6.1.1" - implementation "io.getstream:stream-chat-android-offline:6.1.1" + implementation "io.getstream:stream-chat-android-compose:7.13.0" implementation libs.androidxCoreKtx implementation libs.androidxAppCompat implementation libs.materialComponents // Compose - implementation libs.composeCompiler implementation libs.composeUi implementation libs.composeUiTooling implementation libs.composeFoundation diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/CustomAttachmentsApp.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/CustomAttachmentsApp.kt index 2940a968..bb0cd83e 100644 --- a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/CustomAttachmentsApp.kt +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/CustomAttachmentsApp.kt @@ -26,12 +26,10 @@ package io.getstream.chat.android.compose.customattachments import android.app.Application import io.getstream.chat.android.client.ChatClient +import io.getstream.chat.android.client.api.ChatClientConfig import io.getstream.chat.android.client.logger.ChatLogLevel import io.getstream.chat.android.models.UploadAttachmentsNetworkType import io.getstream.chat.android.models.User -import io.getstream.chat.android.offline.plugin.factory.StreamOfflinePluginFactory -import io.getstream.chat.android.state.plugin.config.StatePluginConfig -import io.getstream.chat.android.state.plugin.factory.StreamStatePluginFactory class CustomAttachmentsApp : Application() { override fun onCreate() { @@ -41,18 +39,10 @@ class CustomAttachmentsApp : Application() { } private fun setupStreamSdk() { - val offlinePluginFactory = StreamOfflinePluginFactory(appContext = applicationContext) - val statePluginFactory = StreamStatePluginFactory( - config = StatePluginConfig( - backgroundSyncEnabled = true, - userPresence = true, - ), - appContext = applicationContext, - ) ChatClient.Builder("qx5us2v6xvmh", applicationContext) .logLevel(ChatLogLevel.ALL) .uploadAttachmentsNetworkType(UploadAttachmentsNetworkType.NOT_ROAMING) - .withPlugins(offlinePluginFactory, statePluginFactory) + .config(ChatClientConfig(offlineEnabled = true, userPresence = true)) .build() } diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/ChannelsActivity.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/ChannelsActivity.kt index cb7ad3fc..56884e76 100644 --- a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/ChannelsActivity.kt +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/ChannelsActivity.kt @@ -27,8 +27,7 @@ package io.getstream.chat.android.compose.customattachments.activity import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity -import io.getstream.chat.android.compose.customattachments.ui.dateAttachmentFactory -import io.getstream.chat.android.compose.ui.attachments.StreamAttachmentFactories +import io.getstream.chat.android.compose.customattachments.ui.CustomChatComponentFactory import io.getstream.chat.android.compose.ui.channels.ChannelsScreen import io.getstream.chat.android.compose.ui.theme.ChatTheme @@ -37,13 +36,10 @@ class ChannelsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val customFactories = listOf(dateAttachmentFactory) - val defaultFactories = StreamAttachmentFactories.defaultFactories() - setContent { - ChatTheme(attachmentFactories = customFactories + defaultFactories) { + ChatTheme(componentFactory = CustomChatComponentFactory) { ChannelsScreen( - onItemClick = { channel -> + onChannelClick = { channel -> startActivity(MessagesActivity.getIntent(this, channel.cid)) }, onBackPressed = { finish() } diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/MessagesActivity.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/MessagesActivity.kt index b4eccbd0..3c28c380 100644 --- a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/MessagesActivity.kt +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/MessagesActivity.kt @@ -29,10 +29,8 @@ import android.content.Intent import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity +import io.getstream.chat.android.compose.customattachments.ui.CustomChatComponentFactory import io.getstream.chat.android.compose.customattachments.ui.CustomMessagesScreen -import io.getstream.chat.android.compose.customattachments.ui.dateAttachmentFactory -import io.getstream.chat.android.compose.customattachments.ui.quotedDateAttachmentFactory -import io.getstream.chat.android.compose.ui.attachments.StreamAttachmentFactories import io.getstream.chat.android.compose.ui.theme.ChatTheme class MessagesActivity : AppCompatActivity() { @@ -41,17 +39,8 @@ class MessagesActivity : AppCompatActivity() { super.onCreate(savedInstanceState) val channelId = requireNotNull(intent.getStringExtra(KEY_CHANNEL_ID)) - val customFactories = listOf(dateAttachmentFactory) - val defaultFactories = StreamAttachmentFactories.defaultFactories() - - val customQuotedFactories = listOf(quotedDateAttachmentFactory) - val defaultQuotedFactories = StreamAttachmentFactories.defaultQuotedFactories() - setContent { - ChatTheme( - attachmentFactories = customFactories + defaultFactories, - quotedAttachmentFactories = customQuotedFactories + defaultQuotedFactories - ) { + ChatTheme(componentFactory = CustomChatComponentFactory) { CustomMessagesScreen( channelId = channelId, onBackPressed = { finish() } diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomChatComponentFactory.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomChatComponentFactory.kt new file mode 100644 index 00000000..e0a15105 --- /dev/null +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomChatComponentFactory.kt @@ -0,0 +1,142 @@ +/* + * The MIT License (MIT) + * + * Copyright 2022 Stream.IO, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.getstream.chat.android.compose.customattachments.ui + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import io.getstream.chat.android.compose.customattachments.R +import io.getstream.chat.android.compose.ui.components.ComposerCancelIcon +import io.getstream.chat.android.compose.ui.theme.ChatComponentFactory +import io.getstream.chat.android.compose.ui.theme.ChatTheme +import io.getstream.chat.android.compose.ui.theme.CustomAttachmentContentParams +import io.getstream.chat.android.compose.ui.theme.MessageComposerAttachmentsParams +import io.getstream.chat.android.compose.ui.theme.MessageComposerLeadingContentParams +import io.getstream.chat.android.compose.ui.theme.MessageComposerQuotedMessageParams +import io.getstream.chat.android.compose.ui.theme.MessageQuotedContentParams + +/** + * A custom [ChatComponentFactory] that adds support for date attachments. + * + * It renders date attachments in the message list, in the composer preview and in quoted + * messages, and replaces the composer's leading button with a calendar button. + */ +object CustomChatComponentFactory : ChatComponentFactory { + + @Composable + override fun CustomAttachmentContent(params: CustomAttachmentContentParams) { + if (params.state.message.attachments.any { it.type == "date" }) { + DateAttachmentContent( + modifier = params.modifier, + attachmentState = params.state + ) + } + } + + @Composable + override fun MessageComposerAttachments(params: MessageComposerAttachmentsParams) { + val (dateAttachments, otherAttachments) = params.attachments.partition { it.type == "date" } + + Column(modifier = params.modifier) { + if (dateAttachments.isNotEmpty()) { + DateAttachmentPreviewContent( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + attachments = dateAttachments, + onAttachmentRemoved = params.onAttachmentRemoved + ) + } + if (otherAttachments.isNotEmpty()) { + super.MessageComposerAttachments( + params = params.copy( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + attachments = otherAttachments + ) + ) + } + } + } + + @Composable + override fun MessageQuotedContent(params: MessageQuotedContentParams) { + if (params.message.attachments.any { it.type == "date" }) { + QuotedDateAttachmentContent( + modifier = params.modifier, + message = params.message + ) + } else { + super.MessageQuotedContent(params) + } + } + + @Composable + override fun MessageComposerQuotedMessage(params: MessageComposerQuotedMessageParams) { + if (params.quotedMessage.attachments.any { it.type == "date" }) { + Box(modifier = params.modifier.padding(horizontal = 8.dp)) { + QuotedDateAttachmentContent(message = params.quotedMessage) + + ComposerCancelIcon( + modifier = Modifier + .align(Alignment.TopEnd) + .offset(x = 4.dp, y = (-4).dp), + onClick = params.onCancelClick + ) + } + } else { + super.MessageComposerQuotedMessage(params) + } + } + + @Composable + override fun MessageComposerLeadingContent(params: MessageComposerLeadingContentParams) { + IconButton( + modifier = params.modifier + .size(48.dp) + .padding(12.dp), + content = { + Icon( + painter = painterResource(id = R.drawable.ic_calendar), + contentDescription = null, + tint = ChatTheme.colors.textSecondary + ) + }, + onClick = params.onAttachmentsClick + ) + } +} diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomMessagesScreen.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomMessagesScreen.kt index b4915e39..db3fbe8e 100644 --- a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomMessagesScreen.kt +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomMessagesScreen.kt @@ -32,30 +32,25 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.material.Icon -import androidx.compose.material.IconButton import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.google.android.material.datepicker.MaterialDatePicker import io.getstream.chat.android.client.ChatClient -import io.getstream.chat.android.compose.customattachments.R -import io.getstream.chat.android.compose.ui.messages.MessagesScreen +import io.getstream.chat.android.compose.ui.messages.ChannelScreen import io.getstream.chat.android.compose.ui.messages.composer.MessageComposer -import io.getstream.chat.android.compose.ui.messages.header.MessageListHeader +import io.getstream.chat.android.compose.ui.messages.header.ChannelHeader import io.getstream.chat.android.compose.ui.messages.list.MessageList import io.getstream.chat.android.compose.ui.theme.ChatTheme +import io.getstream.chat.android.compose.viewmodel.messages.ChannelViewModelFactory import io.getstream.chat.android.compose.viewmodel.messages.MessageComposerViewModel import io.getstream.chat.android.compose.viewmodel.messages.MessageListViewModel -import io.getstream.chat.android.compose.viewmodel.messages.MessagesViewModelFactory import io.getstream.chat.android.models.Attachment import io.getstream.chat.android.ui.common.state.messages.MessageMode import io.getstream.chat.android.ui.common.state.messages.Reply @@ -63,7 +58,7 @@ import java.text.DateFormat import java.util.Date /** - * A custom [MessagesScreen] with the support for date attachments. + * A custom [ChannelScreen] with the support for date attachments. * * @param channelId The ID of the opened channel. * @param onBackPressed Handler for the back action. @@ -73,7 +68,7 @@ fun CustomMessagesScreen( channelId: String, onBackPressed: () -> Unit = {} ) { - val factory = MessagesViewModelFactory( + val factory = ChannelViewModelFactory( context = LocalContext.current, channelId = channelId ) @@ -91,7 +86,7 @@ fun CustomMessagesScreen( Scaffold( modifier = Modifier.fillMaxSize(), topBar = { - MessageListHeader( + ChannelHeader( modifier = Modifier.height(56.dp), channel = messageListViewModel.channel, currentUser = currentUser, @@ -107,12 +102,15 @@ fun CustomMessagesScreen( val date = DateFormat .getDateInstance(DateFormat.LONG) .format(Date(it)) + // fallback is the text shown for this attachment in message previews, + // such as the channel list val attachment = Attachment( type = "date", + fallback = date, extraData = mutableMapOf("payload" to date) ) - composerViewModel.addSelectedAttachments(listOf(attachment)) + composerViewModel.addAttachments(listOf(attachment)) } ) } @@ -120,7 +118,7 @@ fun CustomMessagesScreen( MessageList( modifier = Modifier .padding(it) - .background(ChatTheme.colors.appBackground) + .background(ChatTheme.colors.backgroundCoreApp) .fillMaxSize(), viewModel = messageListViewModel, onThreadClick = { message -> @@ -136,7 +134,8 @@ fun CustomMessagesScreen( } /** - * A custom [MessageComposer] with a button that shows [MaterialDatePicker]. + * A custom [MessageComposer] that shows [MaterialDatePicker] when the leading button is clicked. + * The calendar icon for that button is provided by [CustomChatComponentFactory]. * * @param viewModel The ViewModel that provides pieces of data to show in the composer, like the * currently selected integration data or the user input. It also handles sending messages. @@ -154,30 +153,16 @@ private fun CustomMessageComposer( .fillMaxWidth() .wrapContentHeight(), viewModel = viewModel, - integrations = { - IconButton( - modifier = Modifier - .size(48.dp) - .padding(12.dp), - content = { - Icon( - painter = painterResource(id = R.drawable.ic_calendar), - contentDescription = null, - tint = ChatTheme.colors.textLowEmphasis - ) - }, - onClick = { - MaterialDatePicker.Builder - .datePicker() - .build() - .apply { - show(activity.supportFragmentManager, null) - addOnPositiveButtonClickListener { - onDateSelected(it) - } - } + onAttachmentsClick = { + MaterialDatePicker.Builder + .datePicker() + .build() + .apply { + show(activity.supportFragmentManager, null) + addOnPositiveButtonClickListener { + onDateSelected(it) + } } - ) } ) } diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/DateAttachmentFactory.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/DateAttachmentFactory.kt index f5dec19d..561be221 100644 --- a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/DateAttachmentFactory.kt +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/DateAttachmentFactory.kt @@ -44,32 +44,10 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import io.getstream.chat.android.compose.customattachments.R import io.getstream.chat.android.compose.state.messages.attachments.AttachmentState -import io.getstream.chat.android.compose.ui.attachments.AttachmentFactory -import io.getstream.chat.android.compose.ui.components.CancelIcon +import io.getstream.chat.android.compose.ui.components.ComposerCancelIcon import io.getstream.chat.android.compose.ui.theme.ChatTheme import io.getstream.chat.android.models.Attachment -/** - * A custom [AttachmentFactory] that adds support for date attachments. - */ -val dateAttachmentFactory: AttachmentFactory = AttachmentFactory( - canHandle = { attachments -> attachments.any { it.type == "date" } }, - content = @Composable { modifier, attachmentState -> - DateAttachmentContent( - modifier = modifier, - attachmentState = attachmentState - ) - }, - previewContent = { modifier, attachments, onAttachmentRemoved -> - DateAttachmentPreviewContent( - modifier = modifier, - attachments = attachments, - onAttachmentRemoved = onAttachmentRemoved - ) - }, - textFormatter = { attachment -> attachment.extraData["payload"].toString() } -) - /** * Represents the UI shown in the message input preview before sending. * @@ -90,7 +68,7 @@ fun DateAttachmentPreviewContent( modifier = modifier .wrapContentHeight() .clip(RoundedCornerShape(16.dp)) - .background(color = ChatTheme.colors.barsBackground) + .background(color = ChatTheme.colors.backgroundCoreSurfaceDefault) ) { Text( modifier = Modifier @@ -98,12 +76,12 @@ fun DateAttachmentPreviewContent( .padding(16.dp) .fillMaxWidth(), text = formattedDate, - style = ChatTheme.typography.body, + style = ChatTheme.typography.bodyDefault, maxLines = 1, - color = ChatTheme.colors.textHighEmphasis + color = ChatTheme.colors.textPrimary ) - CancelIcon( + ComposerCancelIcon( modifier = Modifier .align(Alignment.TopEnd) .padding(4.dp), @@ -130,8 +108,8 @@ fun DateAttachmentContent( modifier = modifier .fillMaxWidth() .padding(4.dp) - .clip(ChatTheme.shapes.attachment) - .background(ChatTheme.colors.infoAccent) + .clip(RoundedCornerShape(12.dp)) + .background(ChatTheme.colors.accentSuccess) .padding(8.dp) ) { Row( @@ -142,14 +120,14 @@ fun DateAttachmentContent( modifier = Modifier.size(16.dp), painter = painterResource(id = R.drawable.ic_calendar), contentDescription = null, - tint = ChatTheme.colors.textHighEmphasis + tint = ChatTheme.colors.textPrimary ) Text( text = formattedDate, - style = ChatTheme.typography.body, + style = ChatTheme.typography.bodyDefault, maxLines = 1, - color = ChatTheme.colors.textHighEmphasis + color = ChatTheme.colors.textPrimary ) } } diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/QuotedDateAttachmentFactory.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/QuotedDateAttachmentFactory.kt index 5eb2a6b4..35b23cce 100644 --- a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/QuotedDateAttachmentFactory.kt +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/QuotedDateAttachmentFactory.kt @@ -28,6 +28,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -38,39 +39,25 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import io.getstream.chat.android.compose.customattachments.R -import io.getstream.chat.android.compose.state.messages.attachments.AttachmentState -import io.getstream.chat.android.compose.ui.attachments.AttachmentFactory import io.getstream.chat.android.compose.ui.theme.ChatTheme +import io.getstream.chat.android.models.Message import java.text.ParseException import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale -/** - * A custom [AttachmentFactory] that adds support for quoted date attachments. - */ -val quotedDateAttachmentFactory: AttachmentFactory = AttachmentFactory( - canHandle = { attachments -> attachments.any { it.type == "date" } }, - content = @Composable { modifier, attachmentState -> - QuotedDateAttachmentContent( - modifier = modifier, - attachmentState = attachmentState - ) - } -) - /** * Represents the UI shown in the message list for quoted date attachments. * - * @param attachmentState The state of the attachment. + * @param message The quoted message that contains the date attachment. * @param modifier Modifier for styling. */ @Composable fun QuotedDateAttachmentContent( - attachmentState: AttachmentState, + message: Message, modifier: Modifier = Modifier ) { - val attachment = attachmentState.message.attachments.first { it.type == "date" } + val attachment = message.attachments.first { it.type == "date" } val date = attachment.extraData["payload"].toString() val formattedDate = StringBuilder().apply { try { @@ -95,8 +82,8 @@ fun QuotedDateAttachmentContent( Column( modifier = modifier .padding(4.dp) - .clip(ChatTheme.shapes.attachment) - .background(ChatTheme.colors.infoAccent) + .clip(RoundedCornerShape(12.dp)) + .background(ChatTheme.colors.accentSuccess) .padding(8.dp) ) { Column( @@ -106,13 +93,13 @@ fun QuotedDateAttachmentContent( modifier = Modifier.size(16.dp), painter = painterResource(id = R.drawable.ic_calendar), contentDescription = null, - tint = ChatTheme.colors.textHighEmphasis + tint = ChatTheme.colors.textPrimary ) Text( text = formattedDate, - style = ChatTheme.typography.body, - color = ChatTheme.colors.textHighEmphasis, + style = ChatTheme.typography.bodyDefault, + color = ChatTheme.colors.textPrimary, textAlign = TextAlign.Center ) } diff --git a/custom-attachments-message-composer/build.gradle b/custom-attachments-message-composer/build.gradle index 26ad935f..ff9d51f3 100644 --- a/custom-attachments-message-composer/build.gradle +++ b/custom-attachments-message-composer/build.gradle @@ -4,7 +4,7 @@ plugins { } android { - compileSdk 34 + compileSdk 36 defaultConfig { applicationId "io.getstream.chat.android.customattachmentsv2" @@ -27,9 +27,7 @@ android { dependencies { // Stream SDK dependency - implementation "io.getstream:stream-chat-android-ui-components:6.1.1" - implementation "io.getstream:stream-chat-android-state:6.1.1" - implementation "io.getstream:stream-chat-android-offline:6.1.1" + implementation "io.getstream:stream-chat-android-ui-components:7.13.0" implementation libs.androidxCoreKtx implementation libs.androidxActivityKtx diff --git a/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/App.kt b/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/App.kt index 190ddbfa..14695dda 100644 --- a/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/App.kt +++ b/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/App.kt @@ -26,14 +26,12 @@ package io.getstream.chat.android.customattachments import android.app.Application import io.getstream.chat.android.client.ChatClient +import io.getstream.chat.android.client.api.ChatClientConfig import io.getstream.chat.android.client.logger.ChatLogLevel import io.getstream.chat.android.customattachments.factory.DateAttachmentFactory import io.getstream.chat.android.customattachments.factory.DateAttachmentPreviewFactory import io.getstream.chat.android.customattachments.factory.QuotedDateAttachmentFactory import io.getstream.chat.android.models.User -import io.getstream.chat.android.offline.plugin.factory.StreamOfflinePluginFactory -import io.getstream.chat.android.state.plugin.config.StatePluginConfig -import io.getstream.chat.android.state.plugin.factory.StreamStatePluginFactory import io.getstream.chat.android.ui.ChatUI import io.getstream.chat.android.ui.feature.messages.composer.attachment.preview.AttachmentPreviewFactoryManager import io.getstream.chat.android.ui.feature.messages.composer.attachment.preview.factory.FileAttachmentPreviewFactory @@ -49,14 +47,9 @@ class App : Application() { } private fun setupStreamSdk() { - val offlinePluginFactory = StreamOfflinePluginFactory(appContext = applicationContext) - val statePluginFactory = StreamStatePluginFactory( - config = StatePluginConfig(), - appContext = applicationContext, - ) ChatClient.Builder("qx5us2v6xvmh", applicationContext) .logLevel(ChatLogLevel.ALL) - .withPlugins(offlinePluginFactory, statePluginFactory) + .config(ChatClientConfig(offlineEnabled = true, userPresence = true)) .build() ChatUI.attachmentFactoryManager = AttachmentFactoryManager( diff --git a/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/activity/MessagesActivity.kt b/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/activity/MessagesActivity.kt index 1e5d44a6..ceba4371 100644 --- a/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/activity/MessagesActivity.kt +++ b/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/activity/MessagesActivity.kt @@ -47,10 +47,10 @@ import io.getstream.chat.android.ui.common.state.messages.composer.MessageCompos import io.getstream.chat.android.ui.feature.messages.composer.MessageComposerContext import io.getstream.chat.android.ui.feature.messages.composer.MessageComposerViewStyle import io.getstream.chat.android.ui.feature.messages.composer.content.MessageComposerContent +import io.getstream.chat.android.ui.viewmodel.messages.ChannelHeaderViewModel +import io.getstream.chat.android.ui.viewmodel.messages.ChannelViewModelFactory import io.getstream.chat.android.ui.viewmodel.messages.MessageComposerViewModel -import io.getstream.chat.android.ui.viewmodel.messages.MessageListHeaderViewModel import io.getstream.chat.android.ui.viewmodel.messages.MessageListViewModel -import io.getstream.chat.android.ui.viewmodel.messages.MessageListViewModelFactory import io.getstream.chat.android.ui.viewmodel.messages.bindView import java.text.DateFormat import java.util.Date @@ -58,10 +58,10 @@ import java.util.Date class MessagesActivity : AppCompatActivity() { private lateinit var binding: ActivityMessagesBinding - private val factory: MessageListViewModelFactory by lazy { - MessageListViewModelFactory(this, requireNotNull(intent.getStringExtra(EXTRA_CID))) + private val factory: ChannelViewModelFactory by lazy { + ChannelViewModelFactory(this, requireNotNull(intent.getStringExtra(EXTRA_CID))) } - private val messageListHeaderViewModel: MessageListHeaderViewModel by viewModels { factory } + private val channelHeaderViewModel: ChannelHeaderViewModel by viewModels { factory } private val messageListViewModel: MessageListViewModel by viewModels { factory } private val messageComposerViewModel: MessageComposerViewModel by viewModels { factory } @@ -71,18 +71,18 @@ class MessagesActivity : AppCompatActivity() { binding = ActivityMessagesBinding.inflate(layoutInflater) setContentView(binding.root) - messageListHeaderViewModel.bindView(binding.messageListHeaderView, this) + channelHeaderViewModel.bindView(binding.channelHeaderView, this) messageListViewModel.bindView(binding.messageListView, this) messageComposerViewModel.bindView(binding.messageComposerView, this) messageListViewModel.mode.observe(this) { when (it) { is MessageMode.MessageThread -> { - messageListHeaderViewModel.setActiveThread(it.parentMessage) + channelHeaderViewModel.setActiveThread(it.parentMessage) messageComposerViewModel.setMessageMode(MessageMode.MessageThread(it.parentMessage)) } is MessageMode.Normal -> { - messageListHeaderViewModel.resetThread() + channelHeaderViewModel.resetThread() messageComposerViewModel.leaveThread() } } @@ -103,7 +103,7 @@ class MessagesActivity : AppCompatActivity() { val backHandler = { messageListViewModel.onEvent(MessageListViewModel.Event.BackButtonPressed) } - binding.messageListHeaderView.setBackButtonClickListener(backHandler) + binding.channelHeaderView.setBackButtonClickListener(backHandler) onBackPressedDispatcher.addCallback(this) { backHandler() } @@ -130,7 +130,7 @@ class MessagesActivity : AppCompatActivity() { type = "date", extraData = mutableMapOf("payload" to date) ) - messageComposerViewModel.addSelectedAttachments(listOf(attachment)) + messageComposerViewModel.addAttachments(listOf(attachment)) } // Show the date picker dialog when the attachment button is clicked diff --git a/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/factory/DateAttachmentFactory.kt b/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/factory/DateAttachmentFactory.kt index bee455aa..b19cfdd3 100644 --- a/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/factory/DateAttachmentFactory.kt +++ b/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/factory/DateAttachmentFactory.kt @@ -28,7 +28,7 @@ import android.view.LayoutInflater import android.view.ViewGroup import io.getstream.chat.android.customattachments.databinding.ItemDateAttachmentBinding import io.getstream.chat.android.models.Message -import io.getstream.chat.android.ui.feature.messages.list.adapter.MessageListListenerContainer +import io.getstream.chat.android.ui.feature.messages.list.adapter.MessageListListeners import io.getstream.chat.android.ui.feature.messages.list.adapter.viewholder.attachment.AttachmentFactory import io.getstream.chat.android.ui.feature.messages.list.adapter.viewholder.attachment.InnerAttachmentViewHolder @@ -40,7 +40,7 @@ class DateAttachmentFactory : AttachmentFactory { override fun createViewHolder( message: Message, - listeners: MessageListListenerContainer?, + listeners: MessageListListeners?, parent: ViewGroup ): InnerAttachmentViewHolder { return ItemDateAttachmentBinding @@ -50,7 +50,7 @@ class DateAttachmentFactory : AttachmentFactory { class DateAttachmentViewHolder( private val binding: ItemDateAttachmentBinding, - listeners: MessageListListenerContainer? + listeners: MessageListListeners? ) : InnerAttachmentViewHolder(binding.root) { private lateinit var message: Message diff --git a/custom-attachments-message-composer/src/main/res/layout/activity_messages.xml b/custom-attachments-message-composer/src/main/res/layout/activity_messages.xml index 05fec8c8..2659e4f5 100644 --- a/custom-attachments-message-composer/src/main/res/layout/activity_messages.xml +++ b/custom-attachments-message-composer/src/main/res/layout/activity_messages.xml @@ -27,8 +27,8 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + app:layout_constraintTop_toBottomOf="@+id/channelHeaderView" /> { - messageListHeaderViewModel.setActiveThread(it.parentMessage) + channelHeaderViewModel.setActiveThread(it.parentMessage) } is MessageMode.Normal -> { - messageListHeaderViewModel.resetThread() + channelHeaderViewModel.resetThread() } } messageInputViewModel.setMessageMode(it) diff --git a/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/EventDetailsActivity.kt b/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/EventDetailsActivity.kt index 9519a655..9d2dbfce 100644 --- a/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/EventDetailsActivity.kt +++ b/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/EventDetailsActivity.kt @@ -33,9 +33,9 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.SimpleExoPlayer +import io.getstream.chat.android.ui.viewmodel.messages.ChannelViewModelFactory import io.getstream.chat.android.ui.viewmodel.messages.MessageComposerViewModel import io.getstream.chat.android.ui.viewmodel.messages.MessageListViewModel -import io.getstream.chat.android.ui.viewmodel.messages.MessageListViewModelFactory import io.getstream.chat.android.ui.viewmodel.messages.bindView import io.getstream.chat.virtualevent.databinding.ActivityEventDetailsBinding import io.getstream.chat.virtualevent.util.setupToolbar @@ -58,7 +58,7 @@ class EventDetailsActivity : AppCompatActivity() { setContentView(binding.root) val cid = intent.getStringExtra(KEY_EXTRA_CID)!! - val factory = MessageListViewModelFactory(this, cid = cid) + val factory = ChannelViewModelFactory(this, cid = cid) messageListViewModel = factory.create(MessageListViewModel::class.java) messageInputViewModel = factory.create(MessageComposerViewModel::class.java) diff --git a/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/LivestreamMessageItemVhFactory.kt b/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/LivestreamMessageItemVhFactory.kt index f3fce4a9..cfda9e63 100644 --- a/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/LivestreamMessageItemVhFactory.kt +++ b/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/LivestreamMessageItemVhFactory.kt @@ -55,7 +55,7 @@ class PlainTextViewHolder( ), ) : BaseMessageItemViewHolder(binding.root) { - override fun bindData(data: MessageListItem.MessageItem, diff: MessageListItemPayloadDiff?) { + override fun bindData(data: MessageListItem.MessageItem, diff: MessageListItemPayloadDiff) { binding.messageTextView.text = data.message.text binding.usernameTextView.text = data.message.user.name binding.avatarView.setUser(data.message.user) diff --git a/virtual-event-sample/src/main/res/layout/activity_direct_chat.xml b/virtual-event-sample/src/main/res/layout/activity_direct_chat.xml index 8b92797e..d6fa15f1 100644 --- a/virtual-event-sample/src/main/res/layout/activity_direct_chat.xml +++ b/virtual-event-sample/src/main/res/layout/activity_direct_chat.xml @@ -30,16 +30,16 @@ android:layout_height="match_parent" > - diff --git a/whatsapp-clone-sample/build.gradle b/whatsapp-clone-sample/build.gradle index 619b700d..fd659dad 100644 --- a/whatsapp-clone-sample/build.gradle +++ b/whatsapp-clone-sample/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: "androidx.navigation.safeargs.kotlin" android { - compileSdk 34 + compileSdk 36 defaultConfig { applicationId "io.getstream.whatsappclone" @@ -37,9 +37,7 @@ android { dependencies { // Stream SDK dependency - implementation "io.getstream:stream-chat-android-ui-components:6.1.1" - implementation "io.getstream:stream-chat-android-state:6.1.1" - implementation "io.getstream:stream-chat-android-offline:6.1.1" + implementation "io.getstream:stream-chat-android-ui-components:7.13.0" implementation libs.kotlinStdLib implementation libs.androidxViewPager2 diff --git a/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/WhatsApp.kt b/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/WhatsApp.kt index 0c915f7e..9c5d6b75 100644 --- a/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/WhatsApp.kt +++ b/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/WhatsApp.kt @@ -26,25 +26,16 @@ package io.getstream.whatsappclone import android.app.Application import io.getstream.chat.android.client.ChatClient +import io.getstream.chat.android.client.api.ChatClientConfig import io.getstream.chat.android.models.User -import io.getstream.chat.android.offline.plugin.factory.StreamOfflinePluginFactory -import io.getstream.chat.android.state.plugin.config.StatePluginConfig -import io.getstream.chat.android.state.plugin.factory.StreamStatePluginFactory class WhatsApp : Application() { override fun onCreate() { super.onCreate() - val offlinePluginFactory = StreamOfflinePluginFactory( - appContext = this - ) - val statePluginFactory = StreamStatePluginFactory( - config = StatePluginConfig(), - appContext = this, - ) ChatClient.Builder("qx5us2v6xvmh", this) - .withPlugins(offlinePluginFactory, statePluginFactory) + .config(ChatClientConfig(offlineEnabled = true, userPresence = true)) .build() val user = User( diff --git a/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/ui/messages/MessageListFragment.kt b/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/ui/messages/MessageListFragment.kt index 7a8c8882..c1e35287 100644 --- a/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/ui/messages/MessageListFragment.kt +++ b/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/ui/messages/MessageListFragment.kt @@ -38,10 +38,10 @@ import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import io.getstream.chat.android.client.ChatClient import io.getstream.chat.android.ui.ChatUI +import io.getstream.chat.android.ui.viewmodel.messages.ChannelHeaderViewModel +import io.getstream.chat.android.ui.viewmodel.messages.ChannelViewModelFactory import io.getstream.chat.android.ui.viewmodel.messages.MessageComposerViewModel -import io.getstream.chat.android.ui.viewmodel.messages.MessageListHeaderViewModel import io.getstream.chat.android.ui.viewmodel.messages.MessageListViewModel -import io.getstream.chat.android.ui.viewmodel.messages.MessageListViewModelFactory import io.getstream.chat.android.ui.viewmodel.messages.bindView import io.getstream.whatsappclone.R import io.getstream.whatsappclone.databinding.FragmentMessageListBinding @@ -50,8 +50,8 @@ class MessageListFragment : Fragment() { private val args: MessageListFragmentArgs by navArgs() - private val factory: MessageListViewModelFactory by lazy { MessageListViewModelFactory(requireContext(), args.cid) } - private val messageListHeaderViewModel: MessageListHeaderViewModel by viewModels { factory } + private val factory: ChannelViewModelFactory by lazy { ChannelViewModelFactory(requireContext(), args.cid) } + private val channelHeaderViewModel: ChannelHeaderViewModel by viewModels { factory } private val messageListViewModel: MessageListViewModel by viewModels { factory } private val messageInputViewModel: MessageComposerViewModel by viewModels { factory } @@ -101,7 +101,7 @@ class MessageListFragment : Fragment() { setDisplayShowTitleEnabled(false) } - messageListHeaderViewModel.channel.observe(viewLifecycleOwner) { channel -> + channelHeaderViewModel.channel.observe(viewLifecycleOwner) { channel -> val user = ChatClient.instance().clientState.user.value binding.channelNameTextView.text = ChatUI.channelNameFormatter.formatChannelName(channel, user)