Home: start reworking room list.

This commit is contained in:
ganfra 2019-05-17 10:19:19 +02:00 committed by Benoit Marty
parent 275521db70
commit c0fd06fd2d
9 changed files with 106 additions and 47 deletions

View File

@ -23,6 +23,7 @@ import im.vector.riotredesign.core.error.ErrorFormatter
import im.vector.riotredesign.core.resources.LocaleProvider import im.vector.riotredesign.core.resources.LocaleProvider
import im.vector.riotredesign.core.resources.StringArrayProvider import im.vector.riotredesign.core.resources.StringArrayProvider
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.HomeRoomListObservableStore
import im.vector.riotredesign.features.home.group.SelectedGroupStore import im.vector.riotredesign.features.home.group.SelectedGroupStore
import im.vector.riotredesign.features.home.room.list.RoomSummaryComparator import im.vector.riotredesign.features.home.room.list.RoomSummaryComparator
import im.vector.riotredesign.features.notifications.NotificationDrawerManager import im.vector.riotredesign.features.notifications.NotificationDrawerManager
@ -52,6 +53,10 @@ class AppModule(private val context: Context) {
SelectedGroupStore() SelectedGroupStore()
} }


single {
HomeRoomListObservableStore()
}

single { single {
RoomSummaryComparator() RoomSummaryComparator()
} }

View File

@ -29,4 +29,9 @@ object DateProvider {
return LocalDateTime.ofInstant(instant, zoneId) return LocalDateTime.ofInstant(instant, zoneId)
} }


fun currentLocalDateTime(): LocalDateTime {
val instant = Instant.now()
return LocalDateTime.ofInstant(instant, zoneId)
}

} }

View File

@ -25,14 +25,7 @@ import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserControl
import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserPresenter import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserPresenter
import im.vector.riotredesign.features.home.group.GroupSummaryController import im.vector.riotredesign.features.home.group.GroupSummaryController
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotredesign.features.home.room.detail.timeline.factory.CallItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.factory.*
import im.vector.riotredesign.features.home.room.detail.timeline.factory.DefaultItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.factory.MessageItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.factory.RoomHistoryVisibilityItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.factory.RoomMemberItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.factory.RoomNameItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.factory.RoomTopicItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
import im.vector.riotredesign.features.home.room.list.RoomSummaryController import im.vector.riotredesign.features.home.room.list.RoomSummaryController
@ -56,36 +49,36 @@ class HomeModule {
HomeNavigator() HomeNavigator()
} }


scope(HOME_SCOPE) {
HomeRoomListObservableStore()
}

scope(HOME_SCOPE) { scope(HOME_SCOPE) {
HomePermalinkHandler(get()) HomePermalinkHandler(get())
} }


// Fragment scopes // Fragment scopes


factory {
TimelineDateFormatter(get())
}

factory { (fragment: Fragment) -> factory { (fragment: Fragment) ->
val eventHtmlRenderer = EventHtmlRenderer(GlideApp.with(fragment), fragment.requireContext(), get()) val eventHtmlRenderer = EventHtmlRenderer(GlideApp.with(fragment), fragment.requireContext(), get())
val timelineDateFormatter = TimelineDateFormatter(get())
val timelineMediaSizeProvider = TimelineMediaSizeProvider() val timelineMediaSizeProvider = TimelineMediaSizeProvider()
val colorProvider = ColorProvider(fragment.requireContext()) val colorProvider = ColorProvider(fragment.requireContext())
val timelineDateFormatter = get<TimelineDateFormatter>()
val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, timelineDateFormatter, eventHtmlRenderer) val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, timelineDateFormatter, eventHtmlRenderer)


val timelineItemFactory = TimelineItemFactory(messageItemFactory = messageItemFactory, val timelineItemFactory = TimelineItemFactory(messageItemFactory = messageItemFactory,
roomNameItemFactory = RoomNameItemFactory(get()), roomNameItemFactory = RoomNameItemFactory(get()),
roomTopicItemFactory = RoomTopicItemFactory(get()), roomTopicItemFactory = RoomTopicItemFactory(get()),
roomMemberItemFactory = RoomMemberItemFactory(get()), roomMemberItemFactory = RoomMemberItemFactory(get()),
roomHistoryVisibilityItemFactory = RoomHistoryVisibilityItemFactory(get()), roomHistoryVisibilityItemFactory = RoomHistoryVisibilityItemFactory(get()),
callItemFactory = CallItemFactory(get()), callItemFactory = CallItemFactory(get()),
defaultItemFactory = DefaultItemFactory() defaultItemFactory = DefaultItemFactory()
) )
TimelineEventController(timelineDateFormatter, timelineItemFactory, timelineMediaSizeProvider) TimelineEventController(timelineDateFormatter, timelineItemFactory, timelineMediaSizeProvider)
} }


factory { factory {
RoomSummaryController(get()) RoomSummaryController(get(), get())
} }


factory { factory {

View File

@ -29,6 +29,7 @@ import im.vector.matrix.android.api.session.room.model.tag.RoomTag
import im.vector.riotredesign.core.platform.VectorViewModel import im.vector.riotredesign.core.platform.VectorViewModel
import im.vector.riotredesign.core.utils.LiveEvent import im.vector.riotredesign.core.utils.LiveEvent
import im.vector.riotredesign.features.home.HomeRoomListObservableStore import im.vector.riotredesign.features.home.HomeRoomListObservableStore
import io.reactivex.Observable
import org.koin.android.ext.android.get import org.koin.android.ext.android.get


typealias RoomListFilterName = CharSequence typealias RoomListFilterName = CharSequence
@ -50,7 +51,7 @@ class RoomListViewModel(initialState: RoomListViewState,
} }
} }



private val displayMode = initialState.displayMode
private val roomListFilter = BehaviorRelay.createDefault<Option<RoomListFilterName>>(Option.empty()) private val roomListFilter = BehaviorRelay.createDefault<Option<RoomListFilterName>>(Option.empty())


private val _openRoomLiveData = MutableLiveData<LiveEvent<String>>() private val _openRoomLiveData = MutableLiveData<LiveEvent<String>>()
@ -86,15 +87,27 @@ class RoomListViewModel(initialState: RoomListViewState,




private fun observeRoomSummaries() { private fun observeRoomSummaries() {
homeRoomListObservableSource.observe() homeRoomListObservableSource
.observe()
.flatMapSingle {
Observable.fromIterable(it)
.filter(filterByDisplayMode(displayMode))
.toList()
}
.map { buildRoomSummaries(it) } .map { buildRoomSummaries(it) }
.execute { async -> .execute { async ->
copy( copy(asyncRooms = async)
asyncRooms = async
)
} }
} }


private fun filterByDisplayMode(displayMode: RoomListFragment.DisplayMode) = { roomSummary: RoomSummary ->
when (displayMode) {
RoomListFragment.DisplayMode.HOME -> roomSummary.notificationCount > 0
RoomListFragment.DisplayMode.PEOPLE -> roomSummary.isDirect
RoomListFragment.DisplayMode.ROOMS -> !roomSummary.isDirect
}
}

private fun buildRoomSummaries(rooms: List<RoomSummary>): RoomSummaries { private fun buildRoomSummaries(rooms: List<RoomSummary>): RoomSummaries {
val invites = ArrayList<RoomSummary>() val invites = ArrayList<RoomSummary>()
val favourites = ArrayList<RoomSummary>() val favourites = ArrayList<RoomSummary>()

View File

@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotredesign.R import im.vector.riotredesign.R


data class RoomListViewState( data class RoomListViewState(
val displayMode: RoomListFragment.DisplayMode,
val asyncRooms: Async<RoomSummaries> = Uninitialized, val asyncRooms: Async<RoomSummaries> = Uninitialized,
val isInviteExpanded: Boolean = true, val isInviteExpanded: Boolean = true,
val isFavouriteRoomsExpanded: Boolean = true, val isFavouriteRoomsExpanded: Boolean = true,
@ -33,6 +34,8 @@ data class RoomListViewState(
val isServerNoticeRoomsExpanded: Boolean = true val isServerNoticeRoomsExpanded: Boolean = true
) : MvRxState { ) : MvRxState {


constructor(args: RoomListParams) : this(displayMode = args.displayMode)

fun isCategoryExpanded(roomCategory: RoomCategory): Boolean { fun isCategoryExpanded(roomCategory: RoomCategory): Boolean {
return when (roomCategory) { return when (roomCategory) {
RoomCategory.INVITE -> isInviteExpanded RoomCategory.INVITE -> isInviteExpanded

View File

@ -19,9 +19,13 @@ package im.vector.riotredesign.features.home.room.list
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.epoxy.TypedEpoxyController
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.core.resources.DateProvider
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter


class RoomSummaryController(private val stringProvider: StringProvider class RoomSummaryController(private val stringProvider: StringProvider,
private val timelineDateFormatter: TimelineDateFormatter
) : TypedEpoxyController<RoomListViewState>() { ) : TypedEpoxyController<RoomListViewState>() {


var callback: Callback? = null var callback: Callback? = null
@ -76,9 +80,29 @@ class RoomSummaryController(private val stringProvider: StringProvider
val unreadCount = roomSummary.notificationCount val unreadCount = roomSummary.notificationCount
val showHighlighted = roomSummary.highlightCount > 0 val showHighlighted = roomSummary.highlightCount > 0


var lastMessageFormatted: CharSequence = ""
var lastMessageTime: CharSequence = ""
val lastMessage = roomSummary.lastMessage
if (lastMessage != null) {
val date = lastMessage.localDateTime()
val currentData = DateProvider.currentLocalDateTime()
val isSameDay = date.toLocalDate() == currentData.toLocalDate()
//TODO: get formatted
lastMessageFormatted = lastMessage.content?.toString() ?: ""
lastMessageTime = if (isSameDay) {
timelineDateFormatter.formatMessageHour(date)
} else {
//TODO: change this
timelineDateFormatter.formatMessageDay(date)
}


}
roomSummaryItem { roomSummaryItem {
id(roomSummary.roomId) id(roomSummary.roomId)
roomId(roomSummary.roomId) roomId(roomSummary.roomId)
lastEventTime(lastMessageTime)
lastFormattedEvent(lastMessageFormatted)
roomName(roomSummary.displayName) roomName(roomSummary.displayName)
avatarUrl(roomSummary.avatarUrl) avatarUrl(roomSummary.avatarUrl)
showHighlighted(showHighlighted) showHighlighted(showHighlighted)

View File

@ -32,6 +32,8 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {


@EpoxyAttribute lateinit var roomName: CharSequence @EpoxyAttribute lateinit var roomName: CharSequence
@EpoxyAttribute lateinit var roomId: String @EpoxyAttribute lateinit var roomId: String
@EpoxyAttribute lateinit var lastFormattedEvent: CharSequence
@EpoxyAttribute lateinit var lastEventTime: CharSequence
@EpoxyAttribute var avatarUrl: String? = null @EpoxyAttribute var avatarUrl: String? = null
@EpoxyAttribute var unreadCount: Int = 0 @EpoxyAttribute var unreadCount: Int = 0
@EpoxyAttribute var showHighlighted: Boolean = false @EpoxyAttribute var showHighlighted: Boolean = false
@ -40,15 +42,17 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {


override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.unreadCounterBadgeView.render(unreadCount, showHighlighted)
holder.rootView.setOnClickListener { listener?.invoke() } holder.rootView.setOnClickListener { listener?.invoke() }
holder.titleView.text = roomName holder.titleView.text = roomName
holder.lastEventTimeView.text = lastEventTime
holder.lastEventView.text = lastFormattedEvent
AvatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView) AvatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView)
} }


class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {
val unreadCounterBadgeView by bind<UnreadCounterBadgeView>(R.id.roomUnreadCounterBadgeView)
val titleView by bind<TextView>(R.id.roomNameView) val titleView by bind<TextView>(R.id.roomNameView)
val lastEventView by bind<TextView>(R.id.roomLastEventView)
val lastEventTimeView by bind<TextView>(R.id.roomLastEventTimeView)
val avatarImageView by bind<ImageView>(R.id.roomAvatarImageView) val avatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
val rootView by bind<ViewGroup>(R.id.itemRoomLayout) val rootView by bind<ViewGroup>(R.id.itemRoomLayout)
} }

View File

@ -9,7 +9,8 @@
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:minHeight="48dp" android:paddingBottom="16dp"
android:paddingTop="16dp"
android:paddingStart="8dp" android:paddingStart="8dp"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingEnd="16dp" android:paddingEnd="16dp"
@ -17,8 +18,8 @@


<ImageView <ImageView
android:id="@+id/roomAvatarImageView" android:id="@+id/roomAvatarImageView"
android:layout_width="32dp" android:layout_width="40dp"
android:layout_height="32dp" android:layout_height="40dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginLeft="16dp" android:layout_marginLeft="16dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@ -33,30 +34,38 @@
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:duplicateParentState="true" android:duplicateParentState="true"
android:textColor="@color/color_room_title" android:textColor="@color/black_87"
android:textSize="14sp" android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/roomLastEventTimeView"
app:layout_constraintEnd_toStartOf="@+id/roomUnreadCounterBadgeView"
app:layout_constraintStart_toEndOf="@id/roomAvatarImageView" app:layout_constraintStart_toEndOf="@id/roomAvatarImageView"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/full_names" /> tools:text="@tools:sample/full_names" />


<im.vector.riotredesign.features.home.room.list.UnreadCounterBadgeView <TextView
android:id="@+id/roomUnreadCounterBadgeView" android:id="@+id/roomLastEventView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/black_38"
android:textSize="14sp"
android:maxLines="2"
android:ellipsize="end"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/roomNameView"
app:layout_constraintTop_toBottomOf="@+id/roomNameView"
tools:text="@tools:sample/lorem/random" />

<TextView
android:id="@+id/roomLastEventTimeView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:layout_marginStart="8dp"
android:minWidth="24dp" android:layout_marginLeft="8dp"
android:minHeight="24dp" android:textColor="@color/black_38"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:textColor="@android:color/white"
android:textSize="12sp" android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBaseline_toBaselineOf="@id/messageMemberNameView"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toEndOf="@id/messageMemberNameView"
tools:background="@drawable/bg_unread_highlight" tools:text="@tools:sample/date/hhmm" />
tools:text="115" />


</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -16,6 +16,9 @@
<color name="pale_grey_two">#ebedf8</color> <color name="pale_grey_two">#ebedf8</color>
<color name="brown_grey">#a5a5a5</color> <color name="brown_grey">#a5a5a5</color>
<color name="grey_lynch">#61708B</color> <color name="grey_lynch">#61708B</color>
<color name="black_87">#de000000</color>
<color name="black_38">#61000000</color>
<color name="black_37">#5d000000</color>


<color name="black">#000000</color> <color name="black">#000000</color>
<color name="black_87">#de000000</color> <color name="black_87">#de000000</color>