diff --git a/changelog.d/5959.bugfix b/changelog.d/5959.bugfix new file mode 100644 index 0000000000..c4d20b7f39 --- /dev/null +++ b/changelog.d/5959.bugfix @@ -0,0 +1 @@ +Multiple threads improvement (mainly UI) diff --git a/library/ui-styles/src/main/res/values/colors.xml b/library/ui-styles/src/main/res/values/colors.xml index 2104b49ab5..e72d02f51e 100644 --- a/library/ui-styles/src/main/res/values/colors.xml +++ b/library/ui-styles/src/main/res/values/colors.xml @@ -144,4 +144,9 @@ #17191C #FF4B55 + + + @color/palette_white + @color/palette_black_950 + diff --git a/library/ui-styles/src/main/res/values/dimens.xml b/library/ui-styles/src/main/res/values/dimens.xml index 81d5a77297..826cde5eba 100644 --- a/library/ui-styles/src/main/res/values/dimens.xml +++ b/library/ui-styles/src/main/res/values/dimens.xml @@ -40,7 +40,7 @@ 24dp 48dp 48dp - 38dp + 34dp 56dp diff --git a/library/ui-styles/src/main/res/values/theme_dark.xml b/library/ui-styles/src/main/res/values/theme_dark.xml index eeff039b71..733f7e8eb5 100644 --- a/library/ui-styles/src/main/res/values/theme_dark.xml +++ b/library/ui-styles/src/main/res/values/theme_dark.xml @@ -30,6 +30,7 @@ @color/element_system_dark @color/vctr_message_bubble_inbound_dark @color/vctr_message_bubble_outbound_dark + @color/vctr_badge_color_border_dark #61708B diff --git a/library/ui-styles/src/main/res/values/theme_light.xml b/library/ui-styles/src/main/res/values/theme_light.xml index 0c363b583d..77996c8ce5 100644 --- a/library/ui-styles/src/main/res/values/theme_light.xml +++ b/library/ui-styles/src/main/res/values/theme_light.xml @@ -30,6 +30,7 @@ @color/element_background_light @color/vctr_message_bubble_inbound_light @color/vctr_message_bubble_outbound_light + @color/vctr_badge_color_border_light #61708B diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 2bb620623c..635b00c05d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -1529,7 +1529,7 @@ class TimelineFragment @Inject constructor( views.composerLayout.views.composerEmojiButton.isVisible = vectorPreferences.showEmojiKeyboard() - if (isThreadTimeLine() && timelineArgs.threadTimelineArgs?.startsThread == true) { + if (isThreadTimeLine() && timelineArgs.threadTimelineArgs?.showKeyboard == true) { // Show keyboard when the user started a thread views.composerLayout.views.composerEditText.showKeyboard(andRequestFocus = true) } @@ -2443,7 +2443,11 @@ class TimelineFragment @Inject constructor( private fun onReplyInThreadClicked(action: EventSharedAction.ReplyInThread) { if (vectorPreferences.areThreadMessagesEnabled()) { - navigateToThreadTimeline(action.eventId, action.startsThread) + navigateToThreadTimeline( + rootThreadEventId = action.eventId, + startsThread = action.startsThread, + showKeyboard = true + ) } else { displayThreadsBetaOptInDialog() } @@ -2453,7 +2457,7 @@ class TimelineFragment @Inject constructor( * Navigate to Threads timeline for the specified rootThreadEventId * using the ThreadsActivity. */ - private fun navigateToThreadTimeline(rootThreadEventId: String, startsThread: Boolean = false) { + private fun navigateToThreadTimeline(rootThreadEventId: String, startsThread: Boolean = false, showKeyboard: Boolean = false) { analyticsTracker.capture(Interaction.Name.MobileRoomThreadSummaryItem.toAnalyticsInteraction()) context?.let { val roomThreadDetailArgs = ThreadTimelineArgs( @@ -2462,7 +2466,8 @@ class TimelineFragment @Inject constructor( displayName = timelineViewModel.getRoomSummary()?.displayName, avatarUrl = timelineViewModel.getRoomSummary()?.avatarUrl, roomEncryptionTrustLevel = timelineViewModel.getRoomSummary()?.roomEncryptionTrustLevel, - rootThreadEventId = rootThreadEventId + rootThreadEventId = rootThreadEventId, + showKeyboard = showKeyboard ) navigator.openThread(it, roomThreadDetailArgs) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt index 1952e598a6..011258f126 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -121,14 +121,25 @@ class SearchFragment @Inject constructor( override fun onItemClicked(event: Event) = navigateToEvent(event) + override fun onThreadSummaryClicked(event: Event) { + navigateToEvent(event, true) + } + /** * Navigate and highlight the event. If this is a thread event, * user will be redirected to the appropriate thread room * @param event the event to navigate and highlight + * @param forceNavigateToThread force navigate within the thread (ex. when user clicks on thread summary) */ - private fun navigateToEvent(event: Event) { + private fun navigateToEvent(event: Event, forceNavigateToThread: Boolean = false) { val roomId = event.roomId ?: return - event.getRootThreadEventId()?.let { + val rootThreadEventId = if (forceNavigateToThread) { + event.eventId + } else { + event.getRootThreadEventId() + } + + rootThreadEventId?.let { val threadTimelineArgs = ThreadTimelineArgs( roomId = roomId, displayName = fragmentArgs.roomDisplayName, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt index 913e440a20..81e4d8fd5f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt @@ -58,6 +58,7 @@ class SearchResultController @Inject constructor( interface Listener { fun onItemClicked(event: Event) + fun onThreadSummaryClicked(event: Event) fun loadMore() } @@ -134,6 +135,7 @@ class SearchResultController @Inject constructor( .threadSummaryFormatted(displayableEventFormatter.formatThreadSummary(event.threadDetails?.threadSummaryLatestEvent).toString()) .areThreadMessagesEnabled(userPreferencesProvider.areThreadMessagesEnabled()) .listener { listener?.onItemClicked(eventAndSender.event) } + .threadSummaryListener { listener?.onThreadSummaryClicked(eventAndSender.event) } .let { result.add(it) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt index 3e141ab0e9..d92dcdd3ef 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt @@ -46,6 +46,7 @@ abstract class SearchResultItem : VectorEpoxyModel() { @EpoxyAttribute var areThreadMessagesEnabled: Boolean = false @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var threadSummaryListener: ClickListener? = null override fun bind(holder: Holder) { super.bind(holder) @@ -66,23 +67,24 @@ abstract class SearchResultItem : VectorEpoxyModel() { val displayName = it.threadSummarySenderInfo?.displayName val avatarUrl = it.threadSummarySenderInfo?.avatarUrl avatarRenderer.render(MatrixItem.UserItem(userId, displayName, avatarUrl), holder.threadSummaryAvatarImageView) + holder.threadSummaryContainer.onClick(threadSummaryListener) } else { showFromThread(holder) } } ?: run { - holder.threadSummaryConstraintLayout.isVisible = false + holder.threadSummaryContainer.isVisible = false holder.fromThreadConstraintLayout.isVisible = false } } } private fun showThreadSummary(holder: Holder, show: Boolean = true) { - holder.threadSummaryConstraintLayout.isVisible = show + holder.threadSummaryContainer.isVisible = show holder.fromThreadConstraintLayout.isVisible = !show } private fun showFromThread(holder: Holder, show: Boolean = true) { - holder.threadSummaryConstraintLayout.isVisible = !show + holder.threadSummaryContainer.isVisible = !show holder.fromThreadConstraintLayout.isVisible = show } @@ -91,7 +93,7 @@ abstract class SearchResultItem : VectorEpoxyModel() { val memberNameView by bind(R.id.messageMemberNameView) val timeView by bind(R.id.messageTimeView) val contentView by bind(R.id.messageContentView) - val threadSummaryConstraintLayout by bind(R.id.searchThreadSummaryConstraintLayout) + val threadSummaryContainer by bind(R.id.searchThreadSummaryContainer) val threadSummaryCounterTextView by bind(R.id.messageThreadSummaryCounterTextView) val threadSummaryAvatarImageView by bind(R.id.messageThreadSummaryAvatarImageView) val threadSummaryInfoTextView by bind(R.id.messageThreadSummaryInfoTextView) diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt index 19419e52de..7756c3c5a5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt @@ -27,5 +27,6 @@ data class ThreadTimelineArgs( val avatarUrl: String?, val roomEncryptionTrustLevel: RoomEncryptionTrustLevel?, val rootThreadEventId: String? = null, - val startsThread: Boolean = false + val startsThread: Boolean = false, + val showKeyboard: Boolean = false ) : Parcelable diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt index 04889f375f..8e762fda96 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt @@ -19,6 +19,7 @@ package im.vector.app.features.home.room.threads.list.views import android.os.Bundle import android.view.LayoutInflater import android.view.Menu +import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup @@ -70,6 +71,16 @@ class ThreadListFragment @Inject constructor( analyticsScreenName = MobileScreen.ScreenName.ThreadList } + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + + menu.findItem(R.id.menu_thread_list_filter)?.let { menuItem -> + menuItem.actionView.setOnClickListener { + onOptionsItemSelected(menuItem) + } + } + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.menu_thread_list_filter -> { @@ -82,6 +93,9 @@ class ThreadListFragment @Inject constructor( override fun onPrepareOptionsMenu(menu: Menu) { withState(threadListViewModel) { state -> + val filterIcon = menu.findItem(R.id.menu_thread_list_filter).actionView + val filterBadge = filterIcon.findViewById(R.id.threadListFilterBadge) + filterBadge.isVisible = state.shouldFilterThreads when (threadListViewModel.canHomeserverUseThreading()) { true -> menu.findItem(R.id.menu_thread_list_filter).isVisible = !state.threadSummaryList.invoke().isNullOrEmpty() false -> menu.findItem(R.id.menu_thread_list_filter).isVisible = !state.rootThreadEventList.invoke().isNullOrEmpty() @@ -146,9 +160,9 @@ class ThreadListFragment @Inject constructor( override fun onThreadSummaryClicked(threadSummary: ThreadSummary) { val roomThreadDetailArgs = ThreadTimelineArgs( - roomId = threadSummary.roomId, - displayName = threadSummary.rootThreadSenderInfo.displayName, - avatarUrl = threadSummary.rootThreadSenderInfo.avatarUrl, + roomId = threadListArgs.roomId, + displayName = threadListArgs.displayName, + avatarUrl = threadListArgs.avatarUrl, roomEncryptionTrustLevel = null, rootThreadEventId = threadSummary.rootEventId ) @@ -157,9 +171,9 @@ class ThreadListFragment @Inject constructor( override fun onThreadListClicked(timelineEvent: TimelineEvent) { val threadTimelineArgs = ThreadTimelineArgs( - roomId = timelineEvent.roomId, - displayName = timelineEvent.senderInfo.displayName, - avatarUrl = timelineEvent.senderInfo.avatarUrl, + roomId = threadListArgs.roomId, + displayName = threadListArgs.displayName, + avatarUrl = threadListArgs.avatarUrl, roomEncryptionTrustLevel = null, rootThreadEventId = timelineEvent.eventId ) diff --git a/vector/src/main/res/drawable/thread_filter_badge.xml b/vector/src/main/res/drawable/thread_filter_badge.xml new file mode 100644 index 0000000000..c9a01197c8 --- /dev/null +++ b/vector/src/main/res/drawable/thread_filter_badge.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + diff --git a/vector/src/main/res/layout/item_search_result.xml b/vector/src/main/res/layout/item_search_result.xml index 3264a7d230..6f6528c93b 100644 --- a/vector/src/main/res/layout/item_search_result.xml +++ b/vector/src/main/res/layout/item_search_result.xml @@ -63,7 +63,7 @@ tools:text="@sample/messages.json/data/message" /> - \ No newline at end of file + diff --git a/vector/src/main/res/layout/view_thread_list_filter.xml b/vector/src/main/res/layout/view_thread_list_filter.xml new file mode 100644 index 0000000000..7bdc994f43 --- /dev/null +++ b/vector/src/main/res/layout/view_thread_list_filter.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + diff --git a/vector/src/main/res/layout/view_thread_room_summary.xml b/vector/src/main/res/layout/view_thread_room_summary.xml index 6eeb62974d..e432f0fa35 100644 --- a/vector/src/main/res/layout/view_thread_room_summary.xml +++ b/vector/src/main/res/layout/view_thread_room_summary.xml @@ -21,7 +21,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="4dp" - android:minEms="1" android:textColor="?vctr_content_secondary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@id/messageThreadSummaryImageView" diff --git a/vector/src/main/res/menu/menu_thread_list.xml b/vector/src/main/res/menu/menu_thread_list.xml index 6da0f80112..d9fb1e7997 100644 --- a/vector/src/main/res/menu/menu_thread_list.xml +++ b/vector/src/main/res/menu/menu_thread_list.xml @@ -5,9 +5,10 @@ + app:showAsAction="always" + tools:visible="true" /> - \ No newline at end of file +