diff --git a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/ChannelsActivity.kt b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/ChannelsActivity.kt index 3e7a84f1517..a7633dae25a 100644 --- a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/ChannelsActivity.kt +++ b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/ChannelsActivity.kt @@ -39,6 +39,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -49,6 +50,8 @@ import io.getstream.chat.android.client.ChatClient import io.getstream.chat.android.compose.sample.ChatApp import io.getstream.chat.android.compose.sample.ChatHelper import io.getstream.chat.android.compose.sample.R +import io.getstream.chat.android.compose.sample.ui.component.AppBottomBar +import io.getstream.chat.android.compose.sample.ui.component.AppBottomBarOption import io.getstream.chat.android.compose.sample.ui.login.UserLoginActivity import io.getstream.chat.android.compose.state.channels.list.ItemState import io.getstream.chat.android.compose.state.channels.list.SearchQuery @@ -60,8 +63,11 @@ import io.getstream.chat.android.compose.ui.channels.list.ChannelItem import io.getstream.chat.android.compose.ui.channels.list.ChannelList import io.getstream.chat.android.compose.ui.components.SearchInput import io.getstream.chat.android.compose.ui.theme.ChatTheme +import io.getstream.chat.android.compose.ui.threads.ThreadList import io.getstream.chat.android.compose.viewmodel.channels.ChannelListViewModel import io.getstream.chat.android.compose.viewmodel.channels.ChannelViewModelFactory +import io.getstream.chat.android.compose.viewmodel.threads.ThreadListViewModel +import io.getstream.chat.android.compose.viewmodel.threads.ThreadsViewModelFactory import io.getstream.chat.android.models.Channel import io.getstream.chat.android.models.Message import io.getstream.chat.android.models.User @@ -70,15 +76,17 @@ import kotlinx.coroutines.launch class ChannelsActivity : BaseConnectedActivity() { - private val factory by lazy { + private val listViewModelFactory by lazy { ChannelViewModelFactory( ChatClient.instance(), QuerySortByField.descByName("last_updated"), null, ) } + private val threadsViewModelFactory by lazy { ThreadsViewModelFactory() } - private val listViewModel: ChannelListViewModel by viewModels { factory } + private val listViewModel: ChannelListViewModel by viewModels { listViewModelFactory } + private val threadsViewModel: ThreadListViewModel by viewModels { threadsViewModelFactory } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -91,34 +99,64 @@ class ChannelsActivity : BaseConnectedActivity() { * or build a custom component yourself, like [MyCustomUi]. */ setContent { + var selectedTab by rememberSaveable { mutableStateOf(AppBottomBarOption.CHATS) } + ChatTheme( dateFormatter = ChatApp.dateFormatter, autoTranslationEnabled = ChatApp.autoTranslationEnabled, allowUIAutomationTest = true, ) { - ChannelsScreen( - viewModelFactory = factory, - title = stringResource(id = R.string.app_name), - isShowingHeader = true, - searchMode = SearchMode.Messages, - onChannelClick = ::openMessages, - onSearchMessageItemClick = ::openMessages, - onBackPressed = ::finish, - onHeaderAvatarClick = { - listViewModel.viewModelScope.launch { - ChatHelper.disconnectUser() - openUserLogin() - } + Scaffold( + bottomBar = { + AppBottomBar( + selectedOption = selectedTab, + onOptionSelected = { selectedTab = it }, + ) }, - onHeaderActionClick = { - listViewModel.refresh() + content = { _ -> + when (selectedTab) { + AppBottomBarOption.CHATS -> ChannelsContent() + AppBottomBarOption.THREADS -> ThreadsContent() + } }, ) + } + } + } + + @Composable + private fun ChannelsContent() { + ChannelsScreen( + viewModelFactory = listViewModelFactory, + title = stringResource(id = R.string.app_name), + isShowingHeader = true, + searchMode = SearchMode.Messages, + onChannelClick = ::openMessages, + onSearchMessageItemClick = ::openMessages, + onBackPressed = ::finish, + onHeaderAvatarClick = { + listViewModel.viewModelScope.launch { + ChatHelper.disconnectUser() + openUserLogin() + } + }, + onHeaderActionClick = { + listViewModel.refresh() + }, + ) // MyCustomUiSimplified() // MyCustomUi() - } - } + } + + @Composable + private fun ThreadsContent() { + ThreadList( + viewModel = threadsViewModel, + modifier = Modifier + .fillMaxSize() + .background(ChatTheme.colors.appBackground), + ) } /** diff --git a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/AppBottomBar.kt b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/AppBottomBar.kt new file mode 100644 index 00000000000..fce775defcb --- /dev/null +++ b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/AppBottomBar.kt @@ -0,0 +1,109 @@ +/* + * 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.sample.ui.component + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Icon +import androidx.compose.material.Text +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.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.getstream.chat.android.compose.sample.R +import io.getstream.chat.android.compose.ui.theme.ChatTheme + +/** + * Defines the possible options of the app bottom bar. + */ +enum class AppBottomBarOption { + CHATS, + THREADS, +} + +/** + * Renders the default app bottom bar for switching between chats/threads. + * + * @param selectedOption The currently selected [AppBottomBarOption]. + * @param onOptionSelected Action when invoked when the user clicks on an [AppBottomBarOption]. + */ +@Composable +fun AppBottomBar( + selectedOption: AppBottomBarOption, + onOptionSelected: (AppBottomBarOption) -> Unit, +) { + Row( + modifier = Modifier + .fillMaxWidth() + .background(ChatTheme.colors.barsBackground), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly, + ) { + AppBottomBarOptionTile( + icon = R.drawable.ic_chats, + text = R.string.app_bottom_bar_chats, + isSelected = selectedOption == AppBottomBarOption.CHATS, + onClick = { onOptionSelected(AppBottomBarOption.CHATS) }, + ) + AppBottomBarOptionTile( + icon = R.drawable.ic_threads, + text = R.string.app_bottom_bar_threads, + isSelected = selectedOption == AppBottomBarOption.THREADS, + onClick = { onOptionSelected(AppBottomBarOption.THREADS) }, + ) + } +} + +@Composable +private fun AppBottomBarOptionTile( + @DrawableRes icon: Int, + @StringRes text: Int, + isSelected: Boolean, + onClick: () -> Unit, +) { + val contentColor = if (isSelected) ChatTheme.colors.textHighEmphasis else ChatTheme.colors.textLowEmphasis + Column( + modifier = Modifier + .clickable { onClick() } + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + painter = painterResource(icon), + contentDescription = null, + tint = contentColor, + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = stringResource(text), + fontSize = 12.sp, + color = contentColor, + ) + } +} diff --git a/stream-chat-android-compose-sample/src/main/res/drawable/ic_chats.xml b/stream-chat-android-compose-sample/src/main/res/drawable/ic_chats.xml new file mode 100644 index 00000000000..e7269e29211 --- /dev/null +++ b/stream-chat-android-compose-sample/src/main/res/drawable/ic_chats.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/stream-chat-android-compose-sample/src/main/res/drawable/ic_threads.xml b/stream-chat-android-compose-sample/src/main/res/drawable/ic_threads.xml new file mode 100644 index 00000000000..9ee15f4f3cd --- /dev/null +++ b/stream-chat-android-compose-sample/src/main/res/drawable/ic_threads.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/stream-chat-android-compose-sample/src/main/res/values/strings.xml b/stream-chat-android-compose-sample/src/main/res/values/strings.xml index 511ab19e7e0..6d2b4a92441 100644 --- a/stream-chat-android-compose-sample/src/main/res/values/strings.xml +++ b/stream-chat-android-compose-sample/src/main/res/values/strings.xml @@ -45,4 +45,8 @@ Owner Member Moderator + + + Chats + Threads \ No newline at end of file diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/threads/ThreadItem.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/threads/ThreadItem.kt index d28e84849b2..1c22ab67c46 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/threads/ThreadItem.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/threads/ThreadItem.kt @@ -114,6 +114,7 @@ private fun ThreadTitle( Icon( painter = painterResource(id = R.drawable.stream_compose_ic_thread), contentDescription = null, + tint = ChatTheme.colors.textHighEmphasis, ) Spacer(modifier = Modifier.width(4.dp)) Text(