diff --git a/changelog.d/3547.feature b/changelog.d/3547.feature new file mode 100644 index 0000000000..8921932067 --- /dev/null +++ b/changelog.d/3547.feature @@ -0,0 +1 @@ +Implements new design for Jump to unread and quick fix visibility issues. \ No newline at end of file diff --git a/library/ui-styles/src/main/res/values/styles_jump_to_unread.xml b/library/ui-styles/src/main/res/values/styles_jump_to_unread.xml new file mode 100644 index 0000000000..21f0ebd5d4 --- /dev/null +++ b/library/ui-styles/src/main/res/values/styles_jump_to_unread.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <attr name="vctr_jump_to_unread_style" format="reference" /> + + <style name="Widget.Vector.JumpToUnread.Base" parent="Widget.MaterialComponents.Chip.Action"> + <item name="android:textAppearance">@style/TextAppearance.Vector.Body.Medium</item> + <item name="chipEndPadding">12dp</item> + <item name="chipIconSize">24dp</item> + <item name="chipMinHeight">44dp</item> + <item name="chipStartPadding">12dp</item> + <item name="closeIconVisible">true</item> + <item name="android:elevation">6dp</item> + <item name="closeIconSize">24dp</item> + </style> + + <style name="Widget.Vector.JumpToUnread.Light" parent="Widget.Vector.JumpToUnread.Base"> + <item name="chipBackgroundColor">@color/element_background_light</item> + <item name="closeIconTint">?vctr_content_secondary</item> + </style> + + <style name="Widget.Vector.JumpToUnread.Dark" parent="Widget.Vector.JumpToUnread.Base"> + <item name="chipBackgroundColor">@color/element_system_dark</item> + <item name="closeIconTint">?vctr_content_quaternary</item> + </style> + +</resources> \ No newline at end of file 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 8fffb9f05f..9d2d37d331 100644 --- a/library/ui-styles/src/main/res/values/theme_dark.xml +++ b/library/ui-styles/src/main/res/values/theme_dark.xml @@ -132,6 +132,9 @@ <item name="vctr_social_login_button_twitter_style">@style/Widget.Vector.Button.Outlined.SocialLogin.Twitter.Dark</item> <item name="vctr_social_login_button_apple_style">@style/Widget.Vector.Button.Outlined.SocialLogin.Apple.Dark</item> <item name="vctr_social_login_button_gitlab_style">@style/Widget.Vector.Button.Outlined.SocialLogin.Gitlab.Dark</item> + + <item name="vctr_jump_to_unread_style">@style/Widget.Vector.JumpToUnread.Dark</item> + </style> <style name="Theme.Vector.Dark" parent="Base.Theme.Vector.Dark" /> 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 41786ca030..90338f6178 100644 --- a/library/ui-styles/src/main/res/values/theme_light.xml +++ b/library/ui-styles/src/main/res/values/theme_light.xml @@ -134,6 +134,9 @@ <item name="vctr_social_login_button_twitter_style">@style/Widget.Vector.Button.Outlined.SocialLogin.Twitter.Light</item> <item name="vctr_social_login_button_apple_style">@style/Widget.Vector.Button.Outlined.SocialLogin.Apple.Light</item> <item name="vctr_social_login_button_gitlab_style">@style/Widget.Vector.Button.Outlined.SocialLogin.Gitlab.Light</item> + + <item name="vctr_jump_to_unread_style">@style/Widget.Vector.JumpToUnread.Light</item> + </style> <style name="Theme.Vector.Light" parent="Base.Theme.Vector.Light" /> diff --git a/vector/src/main/java/im/vector/app/core/ui/views/JumpToReadMarkerView.kt b/vector/src/main/java/im/vector/app/core/ui/views/JumpToReadMarkerView.kt deleted file mode 100644 index 28e23c0e50..0000000000 --- a/vector/src/main/java/im/vector/app/core/ui/views/JumpToReadMarkerView.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2019 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.core.ui.views - -import android.content.Context -import android.util.AttributeSet -import android.view.View -import android.widget.RelativeLayout -import androidx.core.content.ContextCompat -import im.vector.app.R -import im.vector.app.databinding.ViewJumpToReadMarkerBinding - -class JumpToReadMarkerView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : RelativeLayout(context, attrs, defStyleAttr) { - - interface Callback { - fun onJumpToReadMarkerClicked() - fun onClearReadMarkerClicked() - } - - var callback: Callback? = null - - init { - setupView() - } - - private fun setupView() { - inflate(context, R.layout.view_jump_to_read_marker, this) - val views = ViewJumpToReadMarkerBinding.bind(this) - setBackgroundColor(ContextCompat.getColor(context, R.color.notification_accent_color)) - views.jumpToReadMarkerLabelView.setOnClickListener { - callback?.onJumpToReadMarkerClicked() - } - views.closeJumpToReadMarkerView.setOnClickListener { - visibility = View.INVISIBLE - callback?.onClearReadMarkerClicked() - } - } -} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index e1e5efa58c..3d99dc785f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -92,7 +92,6 @@ import im.vector.app.core.resources.ColorProvider import im.vector.app.core.ui.views.ActiveConferenceView import im.vector.app.core.ui.views.CurrentCallsView import im.vector.app.core.ui.views.FailedMessagesWarningView -import im.vector.app.core.ui.views.JumpToReadMarkerView import im.vector.app.core.ui.views.KnownCallsViewHolder import im.vector.app.core.ui.views.NotificationAreaView import im.vector.app.core.utils.Debouncer @@ -237,7 +236,6 @@ class RoomDetailFragment @Inject constructor( VectorBaseFragment<FragmentRoomDetailBinding>(), TimelineEventController.Callback, VectorInviteView.Callback, - JumpToReadMarkerView.Callback, AttachmentTypeSelectorView.Callback, AttachmentsHelper.Callback, GalleryOrCameraDialogHelper.Listener, @@ -354,6 +352,10 @@ class RoomDetailFragment @Inject constructor( renderTombstoneEventHandling(it) } + roomDetailViewModel.selectSubscribe(RoomDetailViewState::canShowJumpToReadMarker, RoomDetailViewState::unreadState) { _, _ -> + updateJumpToReadMarkerViewVisibility() + } + roomDetailViewModel.selectSubscribe(RoomDetailViewState::sendMode, RoomDetailViewState::canSendMessage) { mode, canSend -> if (!canSend) { return@selectSubscribe @@ -723,7 +725,12 @@ class RoomDetailFragment @Inject constructor( } private fun setupJumpToReadMarkerView() { - views.jumpToReadMarkerView.callback = this + views.jumpToReadMarkerView.setOnClickListener { + onJumpToReadMarkerClicked() + } + views.jumpToReadMarkerView.setOnCloseIconClickListener { + roomDetailViewModel.handle(RoomDetailAction.MarkAllAsRead) + } } private fun setupActiveCallView() { @@ -1052,7 +1059,13 @@ class RoomDetailFragment @Inject constructor( timelineEventController.timeline = roomDetailViewModel.timeline views.timelineRecyclerView.trackItemsVisibilityChange() - layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, true) + layoutManager = object : LinearLayoutManager(context, RecyclerView.VERTICAL, true) { + override fun onLayoutCompleted(state: RecyclerView.State?) { + super.onLayoutCompleted(state) + updateJumpToReadMarkerViewVisibility() + jumpToBottomViewVisibilityManager.maybeShowJumpToBottomViewVisibilityWithDelay() + } + } val stateRestorer = LayoutManagerStateRestorer(layoutManager).register() scrollOnNewMessageCallback = ScrollOnNewMessageCallback(layoutManager, timelineEventController) scrollOnHighlightedEventCallback = ScrollOnHighlightedEventCallback(views.timelineRecyclerView, layoutManager, timelineEventController) @@ -1063,8 +1076,6 @@ class RoomDetailFragment @Inject constructor( it.dispatchTo(stateRestorer) it.dispatchTo(scrollOnNewMessageCallback) it.dispatchTo(scrollOnHighlightedEventCallback) - updateJumpToReadMarkerViewVisibility() - jumpToBottomViewVisibilityManager.maybeShowJumpToBottomViewVisibilityWithDelay() } timelineEventController.addModelBuildListener(modelBuildListener) views.timelineRecyclerView.adapter = timelineEventController.adapter @@ -1120,7 +1131,7 @@ class RoomDetailFragment @Inject constructor( is UnreadState.ReadMarkerNotLoaded -> true is UnreadState.HasUnread -> { if (it.canShowJumpToReadMarker) { - val lastVisibleItem = layoutManager.findLastVisibleItemPosition() + val lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition() val positionOfReadMarker = timelineEventController.getPositionOfReadMarker() if (positionOfReadMarker == null) { false @@ -1692,7 +1703,6 @@ class RoomDetailFragment @Inject constructor( } override fun onReadMarkerVisible() { - updateJumpToReadMarkerViewVisibility() roomDetailViewModel.handle(RoomDetailAction.EnterTrackingUnreadMessagesState) } @@ -1951,10 +1961,7 @@ class RoomDetailFragment @Inject constructor( roomDetailViewModel.handle(RoomDetailAction.RejectInvite) } -// JumpToReadMarkerView.Callback - - override fun onJumpToReadMarkerClicked() = withState(roomDetailViewModel) { - views.jumpToReadMarkerView.isVisible = false + private fun onJumpToReadMarkerClicked() = withState(roomDetailViewModel) { if (it.unreadState is UnreadState.HasUnread) { roomDetailViewModel.handle(RoomDetailAction.NavigateToEvent(it.unreadState.firstUnreadEventId, false)) } @@ -1963,10 +1970,6 @@ class RoomDetailFragment @Inject constructor( } } - override fun onClearReadMarkerClicked() { - roomDetailViewModel.handle(RoomDetailAction.MarkAllAsRead) - } - // AttachmentTypeSelectorView.Callback private val typeSelectedActivityResultLauncher = registerForPermissionsResult { allGranted -> diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 69aa1e83d3..2cb0f99c50 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -1243,6 +1243,7 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun handleMarkAllAsRead() { + setState { copy(unreadState = UnreadState.HasNoUnread) } viewModelScope.launch { tryOrNull { room.markAsRead(ReadService.MarkAsReadParams.BOTH) } } @@ -1379,7 +1380,6 @@ class RoomDetailViewModel @AssistedInject constructor( } } .subscribe { - Timber.v("Unread state: $it") setState { copy(unreadState = it) } } .disposeOnClear() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/ScrollOnHighlightedEventCallback.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/ScrollOnHighlightedEventCallback.kt index 5d3a91f18d..7f652a2eea 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/ScrollOnHighlightedEventCallback.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/ScrollOnHighlightedEventCallback.kt @@ -20,7 +20,6 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import im.vector.app.core.platform.DefaultListUpdateCallback import im.vector.app.features.home.room.detail.timeline.TimelineEventController -import timber.log.Timber import java.util.concurrent.atomic.AtomicReference /** @@ -42,19 +41,10 @@ class ScrollOnHighlightedEventCallback(private val recyclerView: RecyclerView, private fun scrollIfNeeded() { val eventId = scheduledEventId.get() ?: return - val positionToScroll = timelineEventController.searchPositionOfEvent(eventId) - if (positionToScroll != null) { - val firstVisibleItem = layoutManager.findFirstCompletelyVisibleItemPosition() - val lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition() - - // Do not scroll it item is already visible - if (positionToScroll !in firstVisibleItem..lastVisibleItem) { - Timber.v("Scroll to $positionToScroll") - recyclerView.stopScroll() - layoutManager.scrollToPosition(positionToScroll) - } - scheduledEventId.set(null) - } + val positionToScroll = timelineEventController.searchPositionOfEvent(eventId) ?: return + recyclerView.stopScroll() + layoutManager.scrollToPosition(positionToScroll) + scheduledEventId.set(null) } fun scheduleScrollTo(eventId: String?) { diff --git a/vector/src/main/res/drawable-hdpi/arrow_up_circle.png b/vector/src/main/res/drawable-hdpi/arrow_up_circle.png deleted file mode 100755 index c7fba081c1..0000000000 Binary files a/vector/src/main/res/drawable-hdpi/arrow_up_circle.png and /dev/null differ diff --git a/vector/src/main/res/drawable-mdpi/arrow_up_circle.png b/vector/src/main/res/drawable-mdpi/arrow_up_circle.png deleted file mode 100755 index aad94e9a4e..0000000000 Binary files a/vector/src/main/res/drawable-mdpi/arrow_up_circle.png and /dev/null differ diff --git a/vector/src/main/res/drawable-xhdpi/arrow_up_circle.png b/vector/src/main/res/drawable-xhdpi/arrow_up_circle.png deleted file mode 100755 index 8973128ccc..0000000000 Binary files a/vector/src/main/res/drawable-xhdpi/arrow_up_circle.png and /dev/null differ diff --git a/vector/src/main/res/drawable-xxhdpi/arrow_up_circle.png b/vector/src/main/res/drawable-xxhdpi/arrow_up_circle.png deleted file mode 100755 index c13179d220..0000000000 Binary files a/vector/src/main/res/drawable-xxhdpi/arrow_up_circle.png and /dev/null differ diff --git a/vector/src/main/res/drawable-xxxhdpi/arrow_up_circle.png b/vector/src/main/res/drawable-xxxhdpi/arrow_up_circle.png deleted file mode 100755 index 61fd2b1e48..0000000000 Binary files a/vector/src/main/res/drawable-xxxhdpi/arrow_up_circle.png and /dev/null differ diff --git a/vector/src/main/res/drawable/ic_jump_to_unread.xml b/vector/src/main/res/drawable/ic_jump_to_unread.xml new file mode 100644 index 0000000000..2c5b8b90c1 --- /dev/null +++ b/vector/src/main/res/drawable/ic_jump_to_unread.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,2C6.4771,2 2,6.4771 2,12C2,17.5228 6.4771,22 12,22C17.5228,22 22,17.5228 22,12C22,6.4771 17.5228,2 12,2ZM11.2929,6.2929C11.3888,6.197 11.4993,6.1247 11.6172,6.0759L12.7071,6.2929L12.7075,6.2933L16.7071,10.2929C17.0976,10.6834 17.0976,11.3166 16.7071,11.7071C16.3166,12.0976 15.6834,12.0976 15.2929,11.7071L13,9.4142L13,17C13,17.5523 12.5523,18 12,18C11.4477,18 11,17.5523 11,17L11,9.4142L8.7071,11.7071C8.3166,12.0976 7.6834,12.0976 7.2929,11.7071C6.9024,11.3166 6.9024,10.6834 7.2929,10.2929L11.2929,6.2929ZM11.6172,6.0759L12.705,6.2908C12.5242,6.1111 12.2751,6 12,6C11.8644,6 11.7351,6.027 11.6172,6.0759Z" + android:fillColor="#0DBD8B" + android:fillType="evenOdd"/> +</vector> diff --git a/vector/src/main/res/layout/fragment_room_detail.xml b/vector/src/main/res/layout/fragment_room_detail.xml index 19d85d5393..59d1fbfb32 100644 --- a/vector/src/main/res/layout/fragment_room_detail.xml +++ b/vector/src/main/res/layout/fragment_room_detail.xml @@ -127,32 +127,22 @@ app:layout_constraintTop_toBottomOf="@id/activeConferenceView" tools:listitem="@layout/item_timeline_event_base" /> - <FrameLayout - android:id="@+id/bannersContainer" - android:layout_width="0dp" + <com.google.android.material.chip.Chip + android:id="@+id/jumpToReadMarkerView" + style="?vctr_jump_to_unread_style" + app:chipIcon="@drawable/ic_jump_to_unread" + app:closeIcon="@drawable/ic_close_24dp" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginTop="24dp" + android:text="@string/room_jump_to_first_unread" + android:visibility="invisible" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/activeConferenceView"> + app:layout_constraintTop_toBottomOf="@id/activeConferenceView" + tools:visibility="visible" /> - <!-- <im.vector.app.features.home.room.detail.widget.RoomWidgetsBannerView--> - <!-- android:id="@+id/roomWidgetsBannerView"--> - <!-- android:layout_width="match_parent"--> - <!-- android:layout_height="wrap_content"--> - <!-- android:layout_marginStart="8dp"--> - <!-- android:layout_marginTop="8dp"--> - <!-- android:layout_marginEnd="8dp"--> - <!-- android:visibility="gone"--> - <!-- tools:visibility="visible" />--> - - <im.vector.app.core.ui.views.JumpToReadMarkerView - android:id="@+id/jumpToReadMarkerView" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:visibility="invisible" - tools:visibility="visible" /> - - </FrameLayout> <im.vector.app.core.ui.views.NotificationAreaView android:id="@+id/notificationAreaView" @@ -213,7 +203,7 @@ android:focusable="true" app:cardCornerRadius="16dp" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/activeConferenceView"> + app:layout_constraintTop_toBottomOf="@id/jumpToReadMarkerView"> <org.webrtc.SurfaceViewRenderer android:id="@+id/activeCallPiP" diff --git a/vector/src/main/res/layout/view_jump_to_read_marker.xml b/vector/src/main/res/layout/view_jump_to_read_marker.xml deleted file mode 100644 index 7850435c2d..0000000000 --- a/vector/src/main/res/layout/view_jump_to_read_marker.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<merge xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@color/notification_accent_color" - tools:parentTag="android.widget.RelativeLayout"> - - <TextView - android:id="@+id/jumpToReadMarkerLabelView" - style="@style/Widget.Vector.TextView.Body" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_toStartOf="@+id/closeJumpToReadMarkerView" - android:background="?attr/selectableItemBackground" - android:drawablePadding="10dp" - android:gravity="center_vertical" - android:paddingStart="16dp" - android:paddingTop="12dp" - android:paddingEnd="16dp" - android:paddingBottom="12dp" - android:text="@string/room_jump_to_first_unread" - android:textColor="?colorOnPrimary" - app:drawableStartCompat="@drawable/arrow_up_circle" /> - - <ImageView - android:id="@+id/closeJumpToReadMarkerView" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_alignTop="@+id/jumpToReadMarkerLabelView" - android:layout_alignBottom="@+id/jumpToReadMarkerLabelView" - android:layout_alignParentEnd="true" - android:background="?attr/selectableItemBackground" - android:contentDescription="@string/action_close" - android:paddingStart="16dp" - android:paddingEnd="16dp" - android:src="@drawable/ic_close_24dp" - app:tint="?colorOnPrimary" /> - -</merge> diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 3f2cc17acc..6908fe5d79 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -783,7 +783,7 @@ <string name="list_members">List members</string> <string name="open_chat_header">Open header</string> <string name="room_sync_in_progress">Syncing…</string> - <string name="room_jump_to_first_unread">Jump to first unread message.</string> + <string name="room_jump_to_first_unread">Jump to unread</string> <!-- Room Preview --> <string name="room_preview_invitation_format">You have been invited to join this room by %s</string>