mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-18 20:29:10 +03:00
Move TypingView into the timeline as another item (#7565)
* Typing view as item in list * Don't show TypingItem if we're showing a forward loader
This commit is contained in:
parent
b47dabba58
commit
008432af36
7 changed files with 98 additions and 33 deletions
1
changelog.d/7496.feature
Normal file
1
changelog.d/7496.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Move TypingView inside the timeline items.
|
|
@ -48,9 +48,4 @@ class TypingMessageView @JvmOverloads constructor(
|
|||
views.typingUserText.text = typingHelper.getNotificationTypingMessage(typingUsers)
|
||||
views.typingUserAvatars.render(typingUsers, avatarRenderer)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
removeAllViews()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1154,7 +1154,6 @@ class TimelineFragment :
|
|||
}
|
||||
val summary = mainState.asyncRoomSummary()
|
||||
renderToolbar(summary)
|
||||
renderTypingMessageNotification(summary, mainState)
|
||||
views.removeJitsiWidgetView.render(mainState)
|
||||
if (mainState.hasFailedSending) {
|
||||
lazyLoadedViews.failedMessagesWarningView(inflateIfNeeded = true, createFailedMessagesWarningCallback())?.isVisible = true
|
||||
|
@ -1230,17 +1229,6 @@ class TimelineFragment :
|
|||
voiceMessageRecorderContainer.isVisible = false
|
||||
}
|
||||
|
||||
private fun renderTypingMessageNotification(roomSummary: RoomSummary?, state: RoomDetailViewState) {
|
||||
if (!isThreadTimeLine() && roomSummary != null) {
|
||||
views.typingMessageView.isInvisible = state.typingUsers.isNullOrEmpty()
|
||||
state.typingUsers
|
||||
?.take(MAX_TYPING_MESSAGE_USERS_COUNT)
|
||||
?.let { senders -> views.typingMessageView.render(senders, avatarRenderer) }
|
||||
} else {
|
||||
views.typingMessageView.isInvisible = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderToolbar(roomSummary: RoomSummary?) {
|
||||
when {
|
||||
isLocalRoom() -> {
|
||||
|
|
|
@ -32,6 +32,7 @@ import im.vector.app.core.extensions.localDateTime
|
|||
import im.vector.app.core.extensions.nextOrNull
|
||||
import im.vector.app.core.extensions.prevOrNull
|
||||
import im.vector.app.core.time.Clock
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.detail.JitsiState
|
||||
import im.vector.app.features.home.room.detail.RoomDetailAction
|
||||
import im.vector.app.features.home.room.detail.RoomDetailViewState
|
||||
|
@ -57,6 +58,7 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageInformationD
|
|||
import im.vector.app.features.home.room.detail.timeline.item.ReactionsSummaryEvents
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptsItem
|
||||
import im.vector.app.features.home.room.detail.timeline.item.TypingItem_
|
||||
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
||||
import im.vector.app.features.media.AttachmentData
|
||||
import im.vector.app.features.media.ImageContentRenderer
|
||||
|
@ -94,6 +96,7 @@ class TimelineEventController @Inject constructor(
|
|||
private val readReceiptsItemFactory: ReadReceiptsItemFactory,
|
||||
private val reactionListFactory: ReactionsSummaryFactory,
|
||||
private val clock: Clock,
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener, EpoxyController.Interceptor {
|
||||
|
||||
/**
|
||||
|
@ -104,7 +107,7 @@ class TimelineEventController @Inject constructor(
|
|||
val highlightedEventId: String? = null,
|
||||
val jitsiState: JitsiState = JitsiState(),
|
||||
val roomSummary: RoomSummary? = null,
|
||||
val rootThreadEventId: String? = null
|
||||
val rootThreadEventId: String? = null,
|
||||
) {
|
||||
|
||||
constructor(state: RoomDetailViewState) : this(
|
||||
|
@ -112,7 +115,7 @@ class TimelineEventController @Inject constructor(
|
|||
highlightedEventId = state.highlightedEventId,
|
||||
jitsiState = state.jitsiState,
|
||||
roomSummary = state.asyncRoomSummary(),
|
||||
rootThreadEventId = state.rootThreadEventId
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
)
|
||||
|
||||
fun isFromThreadTimeline(): Boolean = rootThreadEventId != null
|
||||
|
@ -286,7 +289,7 @@ class TimelineEventController @Inject constructor(
|
|||
|
||||
private val interceptorHelper = TimelineControllerInterceptorHelper(
|
||||
::positionOfReadMarker,
|
||||
adapterPositionMapping
|
||||
adapterPositionMapping,
|
||||
)
|
||||
|
||||
init {
|
||||
|
@ -334,6 +337,12 @@ class TimelineEventController @Inject constructor(
|
|||
.setVisibilityStateChangedListener(Timeline.Direction.FORWARDS)
|
||||
.addWhenLoading(Timeline.Direction.FORWARDS)
|
||||
|
||||
if (!showingForwardLoader) {
|
||||
val typingUsers = partialState.roomSummary?.typingUsers.orEmpty()
|
||||
val typingItem = TypingItem_().id("typing_view").avatarRenderer(avatarRenderer).users(typingUsers)
|
||||
add(typingItem)
|
||||
}
|
||||
|
||||
val timelineModels = getModels()
|
||||
add(timelineModels)
|
||||
if (hasReachedInvite && hasUTD) {
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 im.vector.app.features.home.room.detail.timeline.item
|
||||
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import com.airbnb.epoxy.EpoxyModelWithHolder
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.ui.views.TypingMessageView
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||
|
||||
@EpoxyModelClass
|
||||
abstract class TypingItem : EpoxyModelWithHolder<TypingItem.TypingHolder>() {
|
||||
|
||||
companion object {
|
||||
private const val MAX_TYPING_MESSAGE_USERS_COUNT = 4
|
||||
}
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
lateinit var avatarRenderer: AvatarRenderer
|
||||
|
||||
@EpoxyAttribute
|
||||
var users: List<SenderInfo> = emptyList()
|
||||
|
||||
override fun getDefaultLayout(): Int = R.layout.item_typing_users
|
||||
|
||||
override fun bind(holder: TypingHolder) {
|
||||
super.bind(holder)
|
||||
|
||||
val typingUsers = users.take(MAX_TYPING_MESSAGE_USERS_COUNT)
|
||||
holder.typingView.apply {
|
||||
animate().cancel()
|
||||
val duration = 100L
|
||||
if (typingUsers.isEmpty()) {
|
||||
animate().translationY(height.toFloat())
|
||||
.alpha(0f)
|
||||
.setDuration(duration)
|
||||
.withEndAction {
|
||||
isInvisible = true
|
||||
}.start()
|
||||
} else {
|
||||
isVisible = true
|
||||
|
||||
translationY = height.toFloat()
|
||||
alpha = 0f
|
||||
render(typingUsers, avatarRenderer)
|
||||
animate().translationY(0f)
|
||||
.alpha(1f)
|
||||
.setDuration(duration)
|
||||
.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TypingHolder : VectorEpoxyHolder() {
|
||||
val typingView by bind<TypingMessageView>(R.id.typingMessageView)
|
||||
}
|
||||
}
|
|
@ -85,7 +85,7 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:overScrollMode="always"
|
||||
app:layout_constraintBottom_toTopOf="@id/typingMessageView"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomBarrier"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/removeJitsiWidgetView"
|
||||
|
@ -107,18 +107,6 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/removeJitsiWidgetView" />
|
||||
|
||||
<im.vector.app.core.ui.views.TypingMessageView
|
||||
android:id="@+id/typingMessageView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomBarrier"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/timelineRecyclerView" />
|
||||
|
||||
<im.vector.app.core.ui.views.NotificationAreaView
|
||||
android:id="@+id/notificationAreaView"
|
||||
android:layout_width="0dp"
|
||||
|
|
8
vector/src/main/res/layout/item_typing_users.xml
Normal file
8
vector/src/main/res/layout/item_typing_users.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<im.vector.app.core.ui.views.TypingMessageView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/typingMessageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:visibility="invisible" />
|
Loading…
Add table
Reference in a new issue