Repository: element-hq/element-x-android (language: kotlin)

Issue:
Scroll to end of timeline when sending a new message
As a user that has send a message I want the timeline to scroll to that message at the end of the timeline so that I can have a look at what I just send.


Discussion hints:
If I may chime in, this sounds like it would make reading message backlogs and sending replies as I'm reading much more inconvenient, unless there's a reliable "back to last (un)read" feature coming too, to quickly jump back to where I was right before that automatic scroll.
> unless there's a reliable "back to last (un)read" feature coming too

I think you can (or, rather, will be able to) click on the message you replied to in order to go back up.
Only working for text-messages. Should have the same behaviour w. any message.
> Only working for text-messages. Should have the same behaviour w. any message.

It's not working for images for instance since there is no local echo support when sending images right now on the SDK.
So it's linked to https://github.com/matrix-org/matrix-rust-sdk/issues/1732
Can't you just scroll down when the message has been send?
This sounds like a bad idea for a media which can take time to get back from sync. And scrolling to the bottom as soon as we hit the send button is not great neither to me. So not sure what we can do on this for now.
The app could also move to the bottom of the timeline as soon as there is a new message sent by the user (with the side effect that it will scroll even if the user has sent the message from another session - which could be declared "acceptable" I think).
EIX uses the stick at bottom even for incoming messages
@VolkerJunginger will come back to the solution we should use on both platforms.
I have tested what's the behavior on WhatsApp:
- WhatsApp mobile: scroll to the history
- send a message from WhatsApp Desktop (same account)
The timeline is scrolling to live on WhatsApp mobile.

This is the same if you do it with exchange the 2 WhatsApp clients.

I guess we can do the same on Element then, but I let @VolkerJunginger confirm this.
The WhatsApp behaviour is the same we did on iOS, right? If so lets go with this for now.
Not sure this is exactly what is done on iOS. @pixlwave do you if it automatically scrolls if you receive a message you sent from another session?
Yep @ganfra is correct, we don't scroll for the user's other sessions. We basically send a signal to [scroll to bottom](https://github.com/element-hq/element-x-ios/blob/669141b1193fd2cde06b24fe77ca5c1673dc12ef/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift#L306-L312) whenever the app queues a [text message](https://github.com/element-hq/element-x-ios/blob/669141b1193fd2cde06b24fe77ca5c1673dc12ef/ElementX/Sources/Services/Timeline/TimelineProxy.swift#L347) or finished sending some form of [media](https://github.com/element-hq/element-x-ios/blob/669141b1193fd2cde06b24fe77ca5c1673dc12ef/ElementX/Sources/Services/Timeline/TimelineProxy.swift#L240).

Pull request: Scroll to bottom if the new event is from me, even if sent from another session
This fixes the issue that the timeline does not scroll when sending an attachment to the room, since there is no local echo (yet) for such event.

<!-- Please read [CONTRIBUTING.md](https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md) before submitting your pull request -->
 
## Type of change

- [ ] Feature
- [ ] Bugfix
- [ ] Technical
- [ ] Other :

## Content

<!-- Describe shortly what has been changed -->
Finally implement [this](https://github.com/element-hq/element-x-android/pull/1879#discussion_r1407421943).

## Motivation and context

<!-- Provide link to the corresponding issue if applicable or explain the context -->
Fixes #1877 (this [comment](https://github.com/element-hq/element-x-android/issues/1877#issuecomment-1849544205)) for image, or whatever is sent by the user.

## Screenshots / GIFs

<!--
We have screenshot tests in the project, so attaching screenshots to a PR is not mandatory, as far as there
is a Composable Preview covering the changes. In this case, the change will appear in the file diff.
Note that all the UI composables should be covered by a Composable Preview.

Providing a video of the change is still very useful for the reviewer and for the history of the project.

You can use a table like this to show screenshots comparison.
Uncomment this markdown table below and edit the last line `|||`:
|copy screenshot of before here|copy screenshot of after here|

|Before|After|
|-|-|
|||
 -->

## Tests

<!-- Explain how you tested your development -->

- Step 1
- Step 2
- Step ...

## Tested devices

- [ ] Physical
- [ ] Emulator
- OS version(s):

## Checklist

<!-- Depending on the Pull Request content, it can be acceptable if some of the following checkboxes stay unchecked. -->

- [ ] Changes have been tested on an Android device or Android emulator with API 23
- [ ] UI change has been tested on both light and dark themes
- [ ] Accessibility has been taken into account. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#accessibility
- [ ] Pull request is based on the develop branch
- [ ] Pull request includes a new file under ./changelog.d. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#changelog
- [ ] Pull request includes screenshots or videos if containing UI changes
- [ ] Pull request includes a [sign off](https://matrix-org.github.io/synapse/latest/development/contributing_guide.html#sign-off)
- [ ] You've made a self review of your PR


Test command: ./gradlew :features:messages:impl:testDebugUnitTest --tests "io.element.android.features.messages.impl.timeline.TimelinePresenterTest"

Fix patch:
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt
index 799f6aa1686..718c32c67c9 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt
@@ -213,7 +213,8 @@ class TimelinePresenter @AssistedInject constructor(
             newMostRecentItemId != prevMostRecentItemIdValue
         if (hasNewEvent) {
             val newMostRecentEvent = newMostRecentItem as? TimelineItem.Event
-            val fromMe = newMostRecentEvent?.localSendState != null
+            // Scroll to bottom if the new event is from me, even if sent from another device
+            val fromMe = newMostRecentEvent?.isMine == true
             newEventState.value = if (fromMe) {
                 NewEventState.FromMe
             } else {


Test patch:
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt
index 4316750a6a2..eaf61896fb1 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt
@@ -37,7 +37,6 @@ import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
 import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
 import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
 import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction
-import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
 import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender
 import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
 import io.element.android.libraries.matrix.test.AN_EVENT_ID
@@ -205,7 +204,7 @@ class TimelinePresenterTest {
             consumeItemsUntilPredicate { it.timelineItems.size == 1 }
             // Mimics sending a message, and assert newEventState is FromMe
             timeline.updateTimelineItems { items ->
-                val event = anEventTimelineItem(content = aMessageContent(), localSendState = LocalEventSendState.Sent(AN_EVENT_ID))
+                val event = anEventTimelineItem(content = aMessageContent(), isOwn = true)
                 items + listOf(MatrixTimelineItem.Event(1, event))
             }
             consumeItemsUntilPredicate { it.timelineItems.size == 2 }
@@ -366,7 +365,7 @@ class TimelinePresenterTest {
         messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(),
         endPollAction: EndPollAction = FakeEndPollAction(),
         sendPollResponseAction: SendPollResponseAction = FakeSendPollResponseAction(),
-        ): TimelinePresenter {
+    ): TimelinePresenter {
         return TimelinePresenter(
             timelineItemsFactory = timelineItemsFactory,
             room = FakeMatrixRoom(matrixTimeline = timeline),