Skip to content

Commit

Permalink
[4608] [v5] Navigate to Thread Messages via PNs (#4628)
Browse files Browse the repository at this point in the history
* [4608] Implemented the ability to navigate to thread messages by clicking on push notifications

* [4608] Implemented the ability to navigate to thread messages by clicking on push notifications

* [4608] Implement core and UI side changes allowing more reliable displaying of thread messages when opened by clicking on PNs.

* [4608] Make the load notification worker attempt to fetch the thread parent message if the message that has arrived is a thread message.

* [4608] Appease Detekt

* [4608] Appease Detekt

* [4608] Implement a feature flag for the new `MessageListViewModel` ability to navigate to threads via PNs

* [4608] Implement a feature flag for the new `MessageListViewModel` ability to navigate to threads via PNs

* [4608] Implement a feature flag for the new `MessageListViewModel` ability to navigate to threads via PNs

* [4608] Update the changelog.

* [4608] Switch to using the provided `ChatClient` class parameter

* [4608] Remove leftover logging messages

* [4608] Implement navigating to thread messages inside `MessageListViewModel` and `MessagesScreen`.

* [4608] Appease Detekt

* [4608] Spottles format

* [4608] Spotless format

* [4608] Implement opening threads via push notification clicks without focusing on the message

* [4608] Add a `navigateToThreadViaNotification` boolean flag to `MessageListViewModel` and all relevant classes. This flag controls whether threads will be opened when a thread message arrives via PN and the PN is clicked.

- Also make the `MessageComposerViewModel` follow `MessageListViewModel`'s state when the `MessageListViewModel` enters thread mode.

* [4608] Remove unnecessary annotation

* [4608] Small change for the sake of consistency

* [4608] Generate detekt baseline

* - [4608] Apply code review suggestion
  • Loading branch information
MarinTolic authored Jan 30, 2023
1 parent 950b84c commit 54d1385
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 39 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
### ⬆️ Improved

### ✅ Added
- Added the parameter `messageId: String?` to `MessageListViewModel` and `MessageListViewModelFactory`. If `navigateToThreadViaNotification` is set to true (see the changelog entry below), it will enable navigating to threads upon clicking a push notification triggered by a thread message. [#4612](https://github.com/GetStream/stream-chat-android/pull/4612)
- Added the feature flag boolean `navigateToThreadViaNotification: Boolean` to `MessageListViewModel` and `MessageListViewModelFactory`. If it is set to true and a thread message has been received via push notification, clicking on the notification will make the SDK automatically navigate to the thread. If set to false, the SDK will always navigate to the channel containing the thread without navigating to the thread itself. [#4612](https://github.com/GetStream/stream-chat-android/pull/4612)

### ⚠️ Changed

Expand Down
2 changes: 1 addition & 1 deletion stream-chat-android-compose-sample/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>LongMethod:CustomLoginActivity.kt$CustomLoginActivity$@Composable fun CustomLoginScreen( onBackButtonClick: () -> Unit, onLoginButtonClick: (UserCredentials) -> Unit, )</ID>
<ID>LongMethod:MessagesActivity.kt$MessagesActivity$@OptIn(ExperimentalFoundationApi::class) @Composable fun MyCustomUi()</ID>
<ID>LongMethod:MessagesActivity.kt$MessagesActivity$@Composable fun MyCustomUi()</ID>
<ID>MagicNumber:ChannelsActivity.kt$ChannelsActivity$0.5f</ID>
<ID>MagicNumber:MessagesActivity.kt$MessagesActivity$7f</ID>
<ID>MaxLineLength:MessagesActivity.kt$MessagesActivity$lazyListState = if (listViewModel.currentMessagesState.parentMessageId != null) rememberLazyListState() else lazyListState</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ object ChatHelper {
)
val notificationHandler = NotificationHandlerFactory.createNotificationHandler(
context = context,
newMessageIntent = { _: String, channelType: String, channelId: String ->
newMessageIntent = { messageId: String, channelType: String, channelId: String ->
StartupActivity.createIntent(
context = context,
channelId = "$channelType:$channelId"
channelId = "$channelType:$channelId",
messageId = messageId
)
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,13 @@ class ChannelsActivity : BaseConnectedActivity() {
}

private fun openMessages(channel: Channel) {
startActivity(MessagesActivity.createIntent(this, channel.cid))
startActivity(
MessagesActivity.createIntent(
context = this,
channelId = channel.cid,
messageId = null
)
)
}

private fun openUserLogin() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
Expand Down Expand Up @@ -80,6 +79,7 @@ class MessagesActivity : BaseConnectedActivity() {
context = this,
channelId = intent.getStringExtra(KEY_CHANNEL_ID) ?: "",
deletedMessageVisibility = DeletedMessageVisibility.ALWAYS_VISIBLE,
messageId = intent.getStringExtra(KEY_MESSAGE_ID)
)
}

Expand All @@ -91,21 +91,23 @@ class MessagesActivity : BaseConnectedActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val channelId = intent.getStringExtra(KEY_CHANNEL_ID) ?: return
val messageId = intent.getStringExtra(KEY_MESSAGE_ID)

setContent {
ChatTheme(dateFormatter = ChatApp.dateFormatter) {
MessagesScreen(
channelId = channelId,
onBackPressed = { finish() },
onHeaderActionClick = {}
onHeaderActionClick = {},
messageId = messageId,
navigateToThreadViaNotification = true
)

// MyCustomUi()
}
}
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MyCustomUi() {
val isShowingAttachments = attachmentsPickerViewModel.isShowingAttachments
Expand Down Expand Up @@ -299,10 +301,18 @@ class MessagesActivity : BaseConnectedActivity() {

companion object {
private const val KEY_CHANNEL_ID = "channelId"
private const val KEY_MESSAGE_ID = "messageId"

fun createIntent(context: Context, channelId: String): Intent {
fun createIntent(
context: Context,
channelId: String,
messageId: String?,
): Intent {
return Intent(context, MessagesActivity::class.java).apply {
putExtra(KEY_CHANNEL_ID, channelId)
if (messageId != null) {
putExtra(KEY_MESSAGE_ID, messageId)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,17 @@ class StartupActivity : AppCompatActivity() {
if (intent.hasExtra(KEY_CHANNEL_ID)) {
// Navigating from push, route to the messages screen
val channelId = requireNotNull(intent.getStringExtra(KEY_CHANNEL_ID))
val messageId = intent.getStringExtra(KEY_MESSAGE_ID)

TaskStackBuilder.create(this)
.addNextIntent(ChannelsActivity.createIntent(this))
.addNextIntent(MessagesActivity.createIntent(this, channelId))
.addNextIntent(
MessagesActivity.createIntent(
context = this,
channelId = channelId,
messageId = messageId
)
)
.startActivities()
} else {
// Logged in, navigate to the channels screen
Expand All @@ -63,10 +71,18 @@ class StartupActivity : AppCompatActivity() {

companion object {
private const val KEY_CHANNEL_ID = "channelId"
private const val KEY_MESSAGE_ID = "messageId"

fun createIntent(context: Context, channelId: String): Intent {
fun createIntent(
context: Context,
channelId: String,
messageId: String?,
): Intent {
return Intent(context, StartupActivity::class.java).apply {
putExtra(KEY_CHANNEL_ID, channelId)
if (messageId != null) {
putExtra(KEY_MESSAGE_ID, messageId)
}
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions stream-chat-android-compose/api/stream-chat-android-compose.api
Original file line number Diff line number Diff line change
Expand Up @@ -1300,7 +1300,7 @@ public final class io/getstream/chat/android/compose/ui/components/userreactions
}

public final class io/getstream/chat/android/compose/ui/messages/MessagesScreenKt {
public static final fun MessagesScreen (Ljava/lang/String;IZZZZLio/getstream/chat/android/common/state/DeletedMessageVisibility;Lio/getstream/chat/android/common/state/MessageFooterVisibility;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
public static final fun MessagesScreen (Ljava/lang/String;IZZZZLio/getstream/chat/android/common/state/DeletedMessageVisibility;Lio/getstream/chat/android/common/state/MessageFooterVisibility;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLandroidx/compose/runtime/Composer;III)V
}

public final class io/getstream/chat/android/compose/ui/messages/attachments/AttachmentsPickerKt {
Expand Down Expand Up @@ -1960,8 +1960,8 @@ public final class io/getstream/chat/android/compose/viewmodel/messages/MessageC

public final class io/getstream/chat/android/compose/viewmodel/messages/MessageListViewModel : androidx/lifecycle/ViewModel {
public static final field $stable I
public fun <init> (Lio/getstream/chat/android/client/ChatClient;Ljava/lang/String;Lio/getstream/chat/android/compose/handlers/ClipboardHandler;IZZZJLio/getstream/chat/android/common/state/DeletedMessageVisibility;Lio/getstream/chat/android/common/state/MessageFooterVisibility;)V
public synthetic fun <init> (Lio/getstream/chat/android/client/ChatClient;Ljava/lang/String;Lio/getstream/chat/android/compose/handlers/ClipboardHandler;IZZZJLio/getstream/chat/android/common/state/DeletedMessageVisibility;Lio/getstream/chat/android/common/state/MessageFooterVisibility;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lio/getstream/chat/android/client/ChatClient;Ljava/lang/String;Lio/getstream/chat/android/compose/handlers/ClipboardHandler;IZZZJLio/getstream/chat/android/common/state/DeletedMessageVisibility;Lio/getstream/chat/android/common/state/MessageFooterVisibility;Ljava/lang/String;Z)V
public synthetic fun <init> (Lio/getstream/chat/android/client/ChatClient;Ljava/lang/String;Lio/getstream/chat/android/compose/handlers/ClipboardHandler;IZZZJLio/getstream/chat/android/common/state/DeletedMessageVisibility;Lio/getstream/chat/android/common/state/MessageFooterVisibility;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun banUser (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;)V
public static synthetic fun banUser$default (Lio/getstream/chat/android/compose/viewmodel/messages/MessageListViewModel;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ILjava/lang/Object;)V
public final fun clearNewMessageState ()V
Expand Down Expand Up @@ -2007,8 +2007,8 @@ public final class io/getstream/chat/android/compose/viewmodel/messages/MessageL

public final class io/getstream/chat/android/compose/viewmodel/messages/MessagesViewModelFactory : androidx/lifecycle/ViewModelProvider$Factory {
public static final field $stable I
public fun <init> (Landroid/content/Context;Ljava/lang/String;Lio/getstream/chat/android/client/ChatClient;ZIIJZZLio/getstream/chat/android/common/state/DeletedMessageVisibility;Lio/getstream/chat/android/common/state/MessageFooterVisibility;J)V
public synthetic fun <init> (Landroid/content/Context;Ljava/lang/String;Lio/getstream/chat/android/client/ChatClient;ZIIJZZLio/getstream/chat/android/common/state/DeletedMessageVisibility;Lio/getstream/chat/android/common/state/MessageFooterVisibility;JILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Landroid/content/Context;Ljava/lang/String;Lio/getstream/chat/android/client/ChatClient;ZIIJZZLio/getstream/chat/android/common/state/DeletedMessageVisibility;Lio/getstream/chat/android/common/state/MessageFooterVisibility;JLjava/lang/String;Z)V
public synthetic fun <init> (Landroid/content/Context;Ljava/lang/String;Lio/getstream/chat/android/client/ChatClient;ZIIJZZLio/getstream/chat/android/common/state/DeletedMessageVisibility;Lio/getstream/chat/android/common/state/MessageFooterVisibility;JLjava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun create (Ljava/lang/Class;)Landroidx/lifecycle/ViewModel;
}

1 change: 0 additions & 1 deletion stream-chat-android-compose/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
<ID>LongParameterList:MessageContainer.kt$( messageItem: MessageItemState, onLongItemClick: (Message) -> Unit, onReactionsClick: (Message) -> Unit = {}, onThreadClick: (Message) -> Unit, onGiphyActionClick: (GiphyAction) -> Unit, onQuotedMessageClick: (Message) -> Unit, onImagePreviewResult: (ImagePreviewResult?) -> Unit, )</ID>
<ID>LongParameterList:MessageList.kt$( messageListItem: MessageListItemState, onImagePreviewResult: (ImagePreviewResult?) -> Unit, onThreadClick: (Message) -> Unit, onLongItemClick: (Message) -> Unit, onReactionsClick: (Message) -> Unit = {}, onGiphyActionClick: (GiphyAction) -> Unit, onQuotedMessageClick: (Message) -> Unit, )</ID>
<ID>LongParameterList:Messages.kt$( messagesState: MessagesState, lazyListState: LazyListState, onMessagesStartReached: () -> Unit, onLastVisibleMessageChanged: (Message) -> Unit, onScrolledToBottom: () -> Unit, modifier: Modifier = Modifier, contentPadding: PaddingValues = PaddingValues(vertical = 16.dp), helperContent: @Composable BoxScope.() -> Unit = { DefaultMessagesHelperContent(messagesState, lazyListState) }, loadingMoreContent: @Composable () -> Unit = { DefaultMessagesLoadingMoreIndicator() }, itemContent: @Composable (MessageListItemState) -> Unit, )</ID>
<ID>LongParameterList:MessagesScreen.kt$( context: Context, channelId: String, enforceUniqueReactions: Boolean, messageLimit: Int, showDateSeparators: Boolean, showSystemMessages: Boolean, deletedMessageVisibility: DeletedMessageVisibility, messageFooterVisibility: MessageFooterVisibility, )</ID>
<ID>MagicNumber:AvatarPosition.kt$3</ID>
<ID>MagicNumber:FileAttachmentContent.kt$0.85f</ID>
<ID>MagicNumber:FilesPicker.kt$6f</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
Expand Down Expand Up @@ -108,6 +107,10 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
* @param onBackPressed Handler for when the user taps on the Back button and/or the system
* back button.
* @param onHeaderActionClick Handler for when the user taps on the header action.
* @param messageId The ID of the message which we wish to focus on, if such exists.
* @param navigateToThreadViaNotification If true, when a thread message arrives in a push notification,
* clicking it will automatically open the thread in which the message is located. If false, the SDK will always
* navigate to the channel containing the thread but will not navigate to the thread itself.
*/
@Suppress("LongMethod")
@OptIn(ExperimentalCoroutinesApi::class)
Expand All @@ -123,6 +126,8 @@ public fun MessagesScreen(
messageFooterVisibility: MessageFooterVisibility = MessageFooterVisibility.WithTimeDifference(),
onBackPressed: () -> Unit = {},
onHeaderActionClick: (channel: Channel) -> Unit = {},
messageId: String? = null,
navigateToThreadViaNotification: Boolean = false,
) {
val factory = buildViewModelFactory(
context = LocalContext.current,
Expand All @@ -132,14 +137,22 @@ public fun MessagesScreen(
showSystemMessages = showSystemMessages,
showDateSeparators = showDateSeparators,
deletedMessageVisibility = deletedMessageVisibility,
messageFooterVisibility = messageFooterVisibility
messageFooterVisibility = messageFooterVisibility,
messageId = messageId,
navigateToThreadViaNotification = navigateToThreadViaNotification,
)

val listViewModel = viewModel(MessageListViewModel::class.java, factory = factory)
val composerViewModel = viewModel(MessageComposerViewModel::class.java, factory = factory)
val attachmentsPickerViewModel =
viewModel(AttachmentsPickerViewModel::class.java, factory = factory)

val messageMode = listViewModel.messageMode

if (messageMode is MessageMode.MessageThread) {
composerViewModel.setMessageMode(messageMode)
}

val backAction = {
val isInThread = listViewModel.isInThread
val isShowingOverlay = listViewModel.isShowingOverlay
Expand All @@ -163,7 +176,6 @@ public fun MessagesScreen(
modifier = Modifier.fillMaxSize(),
topBar = {
if (showHeader) {
val messageMode = listViewModel.messageMode
val connectionState by listViewModel.connectionState.collectAsState()
val user by listViewModel.user.collectAsState()

Expand Down Expand Up @@ -393,7 +405,7 @@ private fun BoxScope.MessagesScreenMenus(
* @param selectedMessageState The state of the currently selected message.
* @param selectedMessage The currently selected message.
*/
@OptIn(ExperimentalFoundationApi::class, ExperimentalAnimationApi::class)
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun BoxScope.MessagesScreenReactionsPicker(
listViewModel: MessageListViewModel,
Expand Down Expand Up @@ -569,8 +581,13 @@ private fun MessageDialogs(listViewModel: MessageListViewModel) {
* @param showSystemMessages If we should show system messages or not. * @param deletedMessageVisibility The behavior of deleted messages in the list.
* @param deletedMessageVisibility The behavior of deleted messages in the list and if they're visible or not.
* @param messageFooterVisibility The behavior of message footers in the list and their visibility.
* @param messageId The ID of the message which we wish to focus on, if such exists.
* @param navigateToThreadViaNotification If true, when a thread message arrives in a push notification,
* clicking it will automatically open the thread in which the message is located. If false, the SDK will always
* navigate to the channel containing the thread but will not navigate to the thread itself.
*/
@ExperimentalCoroutinesApi
@Suppress("LongParameterList")
private fun buildViewModelFactory(
context: Context,
channelId: String,
Expand All @@ -580,6 +597,8 @@ private fun buildViewModelFactory(
showSystemMessages: Boolean,
deletedMessageVisibility: DeletedMessageVisibility,
messageFooterVisibility: MessageFooterVisibility,
messageId: String? = null,
navigateToThreadViaNotification: Boolean = false,
): MessagesViewModelFactory {
return MessagesViewModelFactory(
context = context,
Expand All @@ -589,6 +608,8 @@ private fun buildViewModelFactory(
showDateSeparators = showDateSeparators,
showSystemMessages = showSystemMessages,
deletedMessageVisibility = deletedMessageVisibility,
messageFooterVisibility = messageFooterVisibility
messageFooterVisibility = messageFooterVisibility,
messageId = messageId,
navigateToThreadViaNotification = navigateToThreadViaNotification,
)
}
Loading

0 comments on commit 54d1385

Please sign in to comment.