mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-26 11:26:01 +03:00
Add date in view all threads
UI Improvements on threads summary Add View In Room bottom sheet action from within thread timeline root message
This commit is contained in:
parent
722f367690
commit
5e5ce614ef
16 changed files with 103 additions and 37 deletions
|
@ -676,7 +676,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
if (initialState.isThreadTimeline()) {
|
||||
when (itemId) {
|
||||
R.id.menu_thread_timeline_more -> true
|
||||
else -> false
|
||||
else -> false
|
||||
}
|
||||
} else {
|
||||
when (itemId) {
|
||||
|
@ -688,7 +688,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
// Show Join conference button only if there is an active conf id not joined. Otherwise fallback to default video disabled. ^
|
||||
R.id.join_conference -> !state.isWebRTCCallOptionAvailable() && state.jitsiState.confId != null && !state.jitsiState.hasJoined
|
||||
R.id.search -> true
|
||||
R.id.threads -> true
|
||||
R.id.threads -> BuildConfig.THREADING_ENABLED
|
||||
R.id.dev_tools -> vectorPreferences.developerMode()
|
||||
else -> false
|
||||
}
|
||||
|
|
|
@ -2012,6 +2012,13 @@ class TimelineFragment @Inject constructor(
|
|||
requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit)
|
||||
}
|
||||
}
|
||||
is EventSharedAction.ViewInRoom -> {
|
||||
if (!views.voiceMessageRecorderView.isActive()) {
|
||||
handleViewInRoomAction()
|
||||
} else {
|
||||
requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit)
|
||||
}
|
||||
}
|
||||
is EventSharedAction.CopyPermalink -> {
|
||||
val permalink = session.permalinkService().createPermalink(timelineArgs.roomId, action.eventId)
|
||||
copyToClipboard(requireContext(), permalink, false)
|
||||
|
|
|
@ -104,6 +104,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
roomSummary = state.asyncRoomSummary(),
|
||||
rootThreadEventId = state.rootThreadEventId
|
||||
)
|
||||
|
||||
fun isFromThreadTimeline():Boolean = rootThreadEventId != null
|
||||
}
|
||||
|
||||
interface Callback :
|
||||
|
@ -193,7 +195,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
// it's sent by the same user so we are sure we have up to date information.
|
||||
val invalidatedSenderId: String? = currentSnapshot.getOrNull(position)?.senderInfo?.userId
|
||||
val prevDisplayableEventIndex = currentSnapshot.subList(0, position).indexOfLast {
|
||||
timelineEventVisibilityHelper.shouldShowEvent(it, partialState.highlightedEventId, partialState.rootThreadEventId )
|
||||
timelineEventVisibilityHelper.shouldShowEvent(it, partialState.highlightedEventId, partialState.isFromThreadTimeline() )
|
||||
}
|
||||
if (prevDisplayableEventIndex != -1 && currentSnapshot[prevDisplayableEventIndex].senderInfo.userId == invalidatedSenderId) {
|
||||
modelCache[prevDisplayableEventIndex] = null
|
||||
|
@ -370,7 +372,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
val nextEvent = currentSnapshot.nextOrNull(position)
|
||||
val prevEvent = currentSnapshot.prevOrNull(position)
|
||||
val nextDisplayableEvent = currentSnapshot.subList(position + 1, currentSnapshot.size).firstOrNull {
|
||||
timelineEventVisibilityHelper.shouldShowEvent(it, partialState.highlightedEventId, partialState.rootThreadEventId)
|
||||
timelineEventVisibilityHelper.shouldShowEvent(it, partialState.highlightedEventId, partialState.isFromThreadTimeline())
|
||||
}
|
||||
// Should be build if not cached or if model should be refreshed
|
||||
if (modelCache[position] == null || modelCache[position]?.isCacheable == false) {
|
||||
|
@ -452,7 +454,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
return null
|
||||
}
|
||||
// If the event is not shown, we go to the next one
|
||||
if (!timelineEventVisibilityHelper.shouldShowEvent(event, partialState.highlightedEventId, partialState.rootThreadEventId)) {
|
||||
if (!timelineEventVisibilityHelper.shouldShowEvent(event, partialState.highlightedEventId, partialState.isFromThreadTimeline())) {
|
||||
continue
|
||||
}
|
||||
// If the event is sent by us, we update the holder with the eventId and stop the search
|
||||
|
@ -474,7 +476,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
val currentReadReceipts = ArrayList(event.readReceipts).filter {
|
||||
it.user.userId != session.myUserId
|
||||
}
|
||||
if (timelineEventVisibilityHelper.shouldShowEvent(event, partialState.highlightedEventId, partialState.rootThreadEventId)) {
|
||||
if (timelineEventVisibilityHelper.shouldShowEvent(event, partialState.highlightedEventId, partialState.isFromThreadTimeline())) {
|
||||
lastShownEventId = event.eventId
|
||||
}
|
||||
if (lastShownEventId == null) {
|
||||
|
|
|
@ -48,10 +48,14 @@ sealed class EventSharedAction(@StringRes val titleRes: Int,
|
|||
data class Reply(val eventId: String) :
|
||||
EventSharedAction(R.string.reply, R.drawable.ic_reply)
|
||||
|
||||
// TODO add translations
|
||||
data class ReplyInThread(val eventId: String) :
|
||||
// TODO add translations
|
||||
EventSharedAction(R.string.reply_in_thread, R.drawable.ic_reply_in_thread)
|
||||
|
||||
// TODO add translations
|
||||
object ViewInRoom :
|
||||
EventSharedAction(R.string.view_in_room, R.drawable.ic_thread_view_in_room_menu_item)
|
||||
|
||||
data class Share(val eventId: String, val messageContent: MessageContent) :
|
||||
EventSharedAction(R.string.share, R.drawable.ic_share)
|
||||
|
||||
|
|
|
@ -331,6 +331,10 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
add(EventSharedAction.ReplyInThread(eventId))
|
||||
}
|
||||
|
||||
if (canViewInRoom(timelineEvent, messageContent, actionPermissions)) {
|
||||
add(EventSharedAction.ViewInRoom)
|
||||
}
|
||||
|
||||
if (canEdit(timelineEvent, session.myUserId, actionPermissions)) {
|
||||
add(EventSharedAction.Edit(eventId))
|
||||
}
|
||||
|
@ -417,6 +421,11 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not the Reply In Thread bottom sheet setting will be visible
|
||||
* to the user
|
||||
*/
|
||||
// TODO handle reply in thread for images etc
|
||||
private fun canReplyInThread(event: TimelineEvent,
|
||||
messageContent: MessageContent?,
|
||||
actionPermissions: ActionPermissions): Boolean {
|
||||
|
@ -437,6 +446,32 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or no the selected event is a root thread event from within
|
||||
* a thread timeline
|
||||
*/
|
||||
private fun canViewInRoom(event: TimelineEvent,
|
||||
messageContent: MessageContent?,
|
||||
actionPermissions: ActionPermissions): Boolean {
|
||||
// Only event of type EventType.MESSAGE are supported for the moment
|
||||
if (!BuildConfig.THREADING_ENABLED) return false
|
||||
if (!initialState.isFromThreadTimeline) return false
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
if (!actionPermissions.canSendMessage) return false
|
||||
|
||||
return when (messageContent?.msgType) {
|
||||
MessageType.MSGTYPE_TEXT -> event.root.threadDetails?.isRootThread ?: false
|
||||
// MessageType.MSGTYPE_NOTICE,
|
||||
// MessageType.MSGTYPE_EMOTE,
|
||||
// MessageType.MSGTYPE_IMAGE,
|
||||
// MessageType.MSGTYPE_VIDEO,
|
||||
// MessageType.MSGTYPE_AUDIO,
|
||||
// MessageType.MSGTYPE_FILE -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun canQuote(event: TimelineEvent, messageContent: MessageContent?, actionPermissions: ActionPermissions): Boolean {
|
||||
// Only event of type EventType.MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
|
|
|
@ -83,7 +83,7 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde
|
|||
eventIdToHighlight: String?,
|
||||
requestModelBuild: () -> Unit,
|
||||
callback: TimelineEventController.Callback?): MergedMembershipEventsItem_? {
|
||||
val mergedEvents = timelineEventVisibilityHelper.prevSameTypeEvents(items, currentPosition, 2, eventIdToHighlight, partialState.rootThreadEventId)
|
||||
val mergedEvents = timelineEventVisibilityHelper.prevSameTypeEvents(items, currentPosition, 2, eventIdToHighlight, partialState.isFromThreadTimeline())
|
||||
return if (mergedEvents.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
|
|
|
@ -125,6 +125,9 @@ class MessageItemFactory @Inject constructor(
|
|||
pillsPostProcessorFactory.create(roomId)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
fun create(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? {
|
||||
val event = params.event
|
||||
val highlight = params.isHighlighted
|
||||
|
@ -149,7 +152,10 @@ class MessageItemFactory @Inject constructor(
|
|||
// This is an edit event, we should display it when debugging as a notice event
|
||||
return noticeItemFactory.create(params)
|
||||
}
|
||||
val attributes = messageItemAttributesFactory.create(messageContent, informationData, callback, event.root.threadDetails)
|
||||
|
||||
// always hide summary when we are on thread timeline
|
||||
val threadDetails = if(params.isFromThreadTimeline()) null else event.root.threadDetails
|
||||
val attributes = messageItemAttributesFactory.create(messageContent, informationData, callback, threadDetails)
|
||||
|
||||
// val all = event.root.toContent()
|
||||
// val ev = all.toModel<Event>()
|
||||
|
@ -174,6 +180,9 @@ class MessageItemFactory @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun isFromThreadTimeline(params: TimelineItemFactoryParams){
|
||||
params.rootThreadEventId
|
||||
}
|
||||
private fun buildOptionsMessageItem(messageContent: MessageOptionsContent,
|
||||
informationData: MessageInformationData,
|
||||
highlight: Boolean,
|
||||
|
|
|
@ -42,8 +42,8 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
|||
fun create(params: TimelineItemFactoryParams): VectorEpoxyModel<*> {
|
||||
val event = params.event
|
||||
val computedModel = try {
|
||||
if (!timelineEventVisibilityHelper.shouldShowEvent(event, params.highlightedEventId, params.rootThreadEventId)) {
|
||||
return buildEmptyItem(event, params.prevEvent, params.highlightedEventId, params.rootThreadEventId)
|
||||
if (!timelineEventVisibilityHelper.shouldShowEvent(event, params.highlightedEventId, params.isFromThreadTimeline())) {
|
||||
return buildEmptyItem(event, params.prevEvent, params.highlightedEventId, params.isFromThreadTimeline())
|
||||
}
|
||||
when (event.root.getClearType()) {
|
||||
// Message itemsX
|
||||
|
@ -109,11 +109,11 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
|||
Timber.e(throwable, "failed to create message item")
|
||||
defaultItemFactory.create(params, throwable)
|
||||
}
|
||||
return computedModel ?: buildEmptyItem(event, params.prevEvent, params.highlightedEventId, params.rootThreadEventId)
|
||||
return computedModel ?: buildEmptyItem(event, params.prevEvent, params.highlightedEventId, params.isFromThreadTimeline())
|
||||
}
|
||||
|
||||
private fun buildEmptyItem(timelineEvent: TimelineEvent, prevEvent: TimelineEvent?, highlightedEventId: String?, rootThreadEventId: String?): TimelineEmptyItem {
|
||||
val isNotBlank = prevEvent == null || timelineEventVisibilityHelper.shouldShowEvent(prevEvent, highlightedEventId, rootThreadEventId)
|
||||
private fun buildEmptyItem(timelineEvent: TimelineEvent, prevEvent: TimelineEvent?, highlightedEventId: String?, isFromThreadTimeline: Boolean): TimelineEmptyItem {
|
||||
val isNotBlank = prevEvent == null || timelineEventVisibilityHelper.shouldShowEvent(prevEvent, highlightedEventId, isFromThreadTimeline)
|
||||
return TimelineEmptyItem_()
|
||||
.id(timelineEvent.localId)
|
||||
.eventId(timelineEvent.eventId)
|
||||
|
|
|
@ -38,4 +38,6 @@ data class TimelineItemFactoryParams(
|
|||
get() = partialState.rootThreadEventId
|
||||
|
||||
val isHighlighted = highlightedEventId == event.eventId
|
||||
|
||||
fun isFromThreadTimeline(): Boolean = rootThreadEventId != null
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen
|
|||
*
|
||||
* @return a list of timeline events which have sequentially the same type following the next direction.
|
||||
*/
|
||||
private fun nextSameTypeEvents(timelineEvents: List<TimelineEvent>, index: Int, minSize: Int, eventIdToHighlight: String?, rootThreadEventId: String?): List<TimelineEvent> {
|
||||
private fun nextSameTypeEvents(timelineEvents: List<TimelineEvent>, index: Int, minSize: Int, eventIdToHighlight: String?, isFromThreadTimeline: Boolean): List<TimelineEvent> {
|
||||
if (index >= timelineEvents.size - 1) {
|
||||
return emptyList()
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen
|
|||
} else {
|
||||
nextSameDayEvents.subList(0, indexOfFirstDifferentEventType)
|
||||
}
|
||||
val filteredSameTypeEvents = sameTypeEvents.filter { shouldShowEvent(it, eventIdToHighlight, rootThreadEventId) }
|
||||
val filteredSameTypeEvents = sameTypeEvents.filter { shouldShowEvent(it, eventIdToHighlight, isFromThreadTimeline) }
|
||||
if (filteredSameTypeEvents.size < minSize) {
|
||||
return emptyList()
|
||||
}
|
||||
|
@ -77,21 +77,22 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen
|
|||
*
|
||||
* @return a list of timeline events which have sequentially the same type following the prev direction.
|
||||
*/
|
||||
fun prevSameTypeEvents(timelineEvents: List<TimelineEvent>, index: Int, minSize: Int, eventIdToHighlight: String?, rootThreadEventId: String?): List<TimelineEvent> {
|
||||
fun prevSameTypeEvents(timelineEvents: List<TimelineEvent>, index: Int, minSize: Int, eventIdToHighlight: String?, isFromThreadTimeline: Boolean): List<TimelineEvent> {
|
||||
val prevSub = timelineEvents.subList(0, index + 1)
|
||||
return prevSub
|
||||
.reversed()
|
||||
.let {
|
||||
nextSameTypeEvents(it, 0, minSize, eventIdToHighlight, rootThreadEventId)
|
||||
nextSameTypeEvents(it, 0, minSize, eventIdToHighlight, isFromThreadTimeline)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timelineEvent the event to check for visibility
|
||||
* @param highlightedEventId can be checked to force visibility to true
|
||||
* @param rootThreadEventId if this param is null it means we are in the original timeline
|
||||
* @return true if the event should be shown in the timeline.
|
||||
*/
|
||||
fun shouldShowEvent(timelineEvent: TimelineEvent, highlightedEventId: String?, rootThreadEventId: String?): Boolean {
|
||||
fun shouldShowEvent(timelineEvent: TimelineEvent, highlightedEventId: String?, isFromThreadTimeline: Boolean): Boolean {
|
||||
// If show hidden events is true we should always display something
|
||||
if (userPreferencesProvider.shouldShowHiddenEvents()) {
|
||||
return true
|
||||
|
@ -105,14 +106,14 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen
|
|||
}
|
||||
|
||||
// Check for special case where we should hide the event, like redacted, relation, memberships... according to user preferences.
|
||||
return !timelineEvent.shouldBeHidden(rootThreadEventId)
|
||||
return !timelineEvent.shouldBeHidden(isFromThreadTimeline)
|
||||
}
|
||||
|
||||
private fun TimelineEvent.isDisplayable(): Boolean {
|
||||
return TimelineDisplayableEvents.DISPLAYABLE_TYPES.contains(root.getClearType())
|
||||
}
|
||||
|
||||
private fun TimelineEvent.shouldBeHidden(rootThreadEventId: String?): Boolean {
|
||||
private fun TimelineEvent.shouldBeHidden(isFromThreadTimeline: Boolean): Boolean {
|
||||
if (root.isRedacted() && !userPreferencesProvider.shouldShowRedactedMessages()) {
|
||||
return true
|
||||
}
|
||||
|
@ -125,7 +126,7 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen
|
|||
if ((diff.isAvatarChange || diff.isDisplaynameChange) && !userPreferencesProvider.shouldShowAvatarDisplayNameChanges()) return true
|
||||
}
|
||||
|
||||
if(BuildConfig.THREADING_ENABLED && rootThreadEventId == null && root.isThread() && root.getRootThreadEventId() != null){
|
||||
if(BuildConfig.THREADING_ENABLED && !isFromThreadTimeline && root.isThread() && root.getRootThreadEventId() != null){
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -114,9 +114,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
|
|||
threadDetails.threadSummarySenderInfo?.let { senderInfo ->
|
||||
attributes.avatarRenderer.render(MatrixItem.UserItem(senderInfo.userId, senderInfo.displayName, senderInfo.avatarUrl), holder.threadSummaryAvatarImageView)
|
||||
}
|
||||
}
|
||||
}else{
|
||||
holder.threadSummaryConstraintLayout.isVisible = false
|
||||
} ?: run{holder.threadSummaryConstraintLayout.isVisible = false}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ abstract class ThreadSummaryModel : VectorEpoxyModel<ThreadSummaryModel.Holder>(
|
|||
@EpoxyAttribute lateinit var rootMessage: String
|
||||
@EpoxyAttribute lateinit var lastMessage: String
|
||||
@EpoxyAttribute lateinit var lastMessageCounter: String
|
||||
@EpoxyAttribute lateinit var lastMessageMatrixItem: MatrixItem
|
||||
@EpoxyAttribute var lastMessageMatrixItem: MatrixItem? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
|
@ -48,8 +48,10 @@ abstract class ThreadSummaryModel : VectorEpoxyModel<ThreadSummaryModel.Holder>(
|
|||
holder.rootMessageTextView.text = rootMessage
|
||||
|
||||
// Last message summary
|
||||
avatarRenderer.render(lastMessageMatrixItem, holder.lastMessageAvatarImageView)
|
||||
holder.lastMessageAvatarImageView.contentDescription = lastMessageMatrixItem.getBestName()
|
||||
lastMessageMatrixItem?.let {
|
||||
avatarRenderer.render(it, holder.lastMessageAvatarImageView)
|
||||
}
|
||||
holder.lastMessageAvatarImageView.contentDescription = lastMessageMatrixItem?.getBestName()
|
||||
holder.lastMessageTextView.text = lastMessage
|
||||
holder.lastMessageCounterTextView.text = lastMessageCounter
|
||||
}
|
||||
|
|
|
@ -17,13 +17,16 @@
|
|||
package im.vector.app.features.home.room.threads.list.viewmodel
|
||||
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.app.core.date.DateFormatKind
|
||||
import im.vector.app.core.date.VectorDateFormatter
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.threads.list.model.threadSummary
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class ThreadSummaryController @Inject constructor(
|
||||
private val avatarRenderer: AvatarRenderer
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
private val dateFormatter: VectorDateFormatter
|
||||
) : EpoxyController() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
@ -53,12 +56,13 @@ class ThreadSummaryController @Inject constructor(
|
|||
// this one is added to the breadcrumbs
|
||||
safeViewState.rootThreadEventList.invoke()
|
||||
?.forEach { timelineEvent ->
|
||||
val date = dateFormatter.format(timelineEvent.root.originServerTs, DateFormatKind.ROOM_LIST)
|
||||
threadSummary {
|
||||
id(timelineEvent.eventId)
|
||||
avatarRenderer(host.avatarRenderer)
|
||||
matrixItem(timelineEvent.senderInfo.toMatrixItem())
|
||||
title(timelineEvent.senderInfo.displayName)
|
||||
date(timelineEvent.root.ageLocalTs.toString())
|
||||
date(date)
|
||||
rootMessage(timelineEvent.root.getDecryptedUserFriendlyTextSummary())
|
||||
lastMessage(timelineEvent.root.threadDetails?.threadSummaryLatestTextMessage.orEmpty())
|
||||
lastMessageCounter(timelineEvent.root.threadDetails?.numberOfThreads.toString())
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
app:layout_constraintEnd_toStartOf="@id/threadSummaryDateTextView"
|
||||
app:layout_constraintStart_toEndOf="@id/threadSummaryAvatarImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Aris" />
|
||||
tools:text="Aris Kots" />
|
||||
|
||||
|
||||
<TextView
|
||||
|
@ -40,21 +40,21 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:ellipsize="end"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:maxLines="1"
|
||||
android:gravity="end"
|
||||
android:textColor="?vctr_content_secondary"
|
||||
app:layout_constraintBottom_toBottomOf="@id/threadSummaryTitleTextView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/threadSummaryTitleTextView"
|
||||
tools:text="10 minutes ago" />
|
||||
tools:text="10 minutes" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/threadSummaryRootMessageTextView"
|
||||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textColor="?vctr_content_primary"
|
||||
|
@ -67,7 +67,7 @@
|
|||
android:id="@+id/threadSummaryConstraintLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:contentDescription="@string/room_threads_filter"
|
||||
android:maxWidth="496dp"
|
||||
android:minWidth="144dp"
|
||||
|
|
|
@ -20,12 +20,13 @@
|
|||
style="@style/Widget.Vector.TextView.Caption"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minEms="1"
|
||||
android:layout_marginStart="5dp"
|
||||
android:textColor="?vctr_content_secondary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/messageThreadSummaryImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="187" />
|
||||
tools:text="192" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/messageThreadSummaryAvatarImageView"
|
||||
|
|
|
@ -2183,6 +2183,7 @@
|
|||
<string name="edit">Edit</string>
|
||||
<string name="reply">Reply</string>
|
||||
<string name="reply_in_thread">Reply In Thread</string>
|
||||
<string name="view_in_room">View In Room</string>
|
||||
|
||||
<string name="global_retry">Retry</string>
|
||||
<string name="room_list_empty">"Join a room to start using the app."</string>
|
||||
|
|
Loading…
Reference in a new issue