Skip to content

Commit

Permalink
Merge branch 'feature/eric/new-layout-navigation' into community-test…
Browse files Browse the repository at this point in the history
…ing-space-switching-16_08
  • Loading branch information
ericdecanini committed Aug 16, 2022
2 parents 46c68fc + fc301c8 commit 86382d0
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 34 deletions.
8 changes: 5 additions & 3 deletions vector/src/main/java/im/vector/app/SpaceStateHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,13 @@ interface SpaceStateHandler : DefaultLifecycleObserver {
)

/**
* Gets the current backstack of spaces (via their id).
* Gets the Space ID of the space on top of the backstack
*
* null may be an entry in the ArrayDeque to indicate the root space (All Chats)
* May return null to indicate the All Chats space
*/
fun getSpaceBackstack(): ArrayDeque<String?>
fun popSpaceBackstack(): String?

fun getPersistedSpaceBackstack(): List<String?>

/**
* Gets a flow of the selected space for clients to react immediately to space changes.
Expand Down
40 changes: 30 additions & 10 deletions vector/src/main/java/im/vector/app/SpaceStateHandlerImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import im.vector.app.core.utils.BehaviorDataSource
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.UserProperties
import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.ui.UiStateRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -52,13 +53,13 @@ class SpaceStateHandlerImpl @Inject constructor(
private val sessionDataSource: ActiveSessionDataSource,
private val uiStateRepository: UiStateRepository,
private val activeSessionHolder: ActiveSessionHolder,
private val analyticsTracker: AnalyticsTracker
private val analyticsTracker: AnalyticsTracker,
private val vectorPreferences: VectorPreferences,
) : SpaceStateHandler {

private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
private val selectedSpaceDataSource = BehaviorDataSource<Option<RoomSummary>>(Option.empty())
private val selectedSpaceFlow = selectedSpaceDataSource.stream()
private val spaceBackstack = ArrayDeque<String?>()

override fun getCurrentSpace(): RoomSummary? {
return selectedSpaceDataSource.currentValue?.orNull()?.let { spaceSummary ->
Expand All @@ -73,26 +74,26 @@ class SpaceStateHandlerImpl @Inject constructor(
isForwardNavigation: Boolean,
) {
val activeSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return
val currentSpace = selectedSpaceDataSource.currentValue?.orNull()
val spaceSummary = spaceId?.let { activeSession.getRoomSummary(spaceId) }
val sameSpaceSelected = currentSpace != null && spaceId == currentSpace.roomId
val spaceToLeave = selectedSpaceDataSource.currentValue?.orNull()
val spaceToSet = spaceId?.let { activeSession.getRoomSummary(spaceId) }
val sameSpaceSelected = spaceId == spaceToLeave?.roomId

if (sameSpaceSelected) {
return
}

if (isForwardNavigation) {
spaceBackstack.addLast(currentSpace?.roomId)
addToBackstack(spaceToLeave, spaceToSet)
}

if (persistNow) {
uiStateRepository.storeSelectedSpace(spaceSummary?.roomId, activeSession.sessionId)
uiStateRepository.storeSelectedSpace(spaceToSet?.roomId, activeSession.sessionId)
}

if (spaceSummary == null) {
if (spaceToSet == null) {
selectedSpaceDataSource.post(Option.empty())
} else {
selectedSpaceDataSource.post(Option.just(spaceSummary))
selectedSpaceDataSource.post(Option.just(spaceToSet))
}

if (spaceId != null) {
Expand All @@ -104,6 +105,17 @@ class SpaceStateHandlerImpl @Inject constructor(
}
}

private fun addToBackstack(spaceToLeave: RoomSummary?, spaceToSet: RoomSummary?) {
// Only add to the persisted backstack if the space to set is not All Chats, else reset the persisted stack
if (spaceToSet != null) {
val currentPersistedBackstack = vectorPreferences.getPersistedSpaceBackstack().toMutableList()
currentPersistedBackstack.add(spaceToLeave?.roomId)
vectorPreferences.setPersistedSpaceBackstack(currentPersistedBackstack)
} else {
vectorPreferences.setPersistedSpaceBackstack(emptyList())
}
}

private fun observeActiveSession() {
sessionDataSource.stream()
.distinctUntilChanged()
Expand All @@ -127,7 +139,15 @@ class SpaceStateHandlerImpl @Inject constructor(
}.launchIn(session.coroutineScope)
}

override fun getSpaceBackstack() = spaceBackstack
override fun popSpaceBackstack(): String? {
vectorPreferences.getPersistedSpaceBackstack().toMutableList().apply {
val poppedSpaceId = removeLast()
vectorPreferences.setPersistedSpaceBackstack(this)
return poppedSpaceId
}
}

override fun getPersistedSpaceBackstack() = vectorPreferences.getPersistedSpaceBackstack()

override fun getSelectedSpaceFlow() = selectedSpaceFlow

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class HomeDetailFragment @Inject constructor(
}

private fun navigateBack() {
val previousSpaceId = spaceStateHandler.getSpaceBackstack().removeLastOrNull()
val previousSpaceId = spaceStateHandler.popSpaceBackstack()
val parentSpaceId = spaceStateHandler.getCurrentSpace()?.flattenParentIds?.lastOrNull()
setCurrentSpace(previousSpaceId ?: parentSpaceId)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ class NewHomeDetailFragment @Inject constructor(

private val viewModel: HomeDetailViewModel by fragmentViewModel()
private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel()
private val unreadMessagesSharedViewModel: UnreadMessagesSharedViewModel by activityViewModel()
private val serverBackupStatusViewModel: ServerBackupStatusViewModel by activityViewModel()

private lateinit var sharedActionViewModel: HomeSharedActionViewModel
Expand Down Expand Up @@ -175,17 +174,6 @@ class NewHomeDetailFragment @Inject constructor(
}
}

private fun navigateBack() {
val previousSpaceId = spaceStateHandler.getSpaceBackstack().removeLastOrNull()
val parentSpaceId = spaceStateHandler.getCurrentSpace()?.flattenParentIds?.lastOrNull()
setCurrentSpace(previousSpaceId ?: parentSpaceId)
}

private fun setCurrentSpace(spaceId: String?) {
spaceStateHandler.setCurrentSpace(spaceId, isForwardNavigation = false)
sharedActionViewModel.post(HomeActivitySharedAction.OnCloseSpace)
}

private fun handleCallStarted() {
dismissLoadingDialog()
val fragmentTag = HomeTab.DialPad.toFragmentTag()
Expand Down Expand Up @@ -449,10 +437,11 @@ class NewHomeDetailFragment @Inject constructor(
return this
}

override fun onBackPressed(toolbarButton: Boolean) = if (spaceStateHandler.getCurrentSpace() != null) {
navigateBack()
override fun onBackPressed(toolbarButton: Boolean) = try {
val lastSpace = spaceStateHandler.popSpaceBackstack()
spaceStateHandler.setCurrentSpace(lastSpace, isForwardNavigation = false)
true
} else {
} catch (e: NoSuchElementException) {
false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class VectorPreferences @Inject constructor(
const val SETTINGS_ALLOW_INTEGRATIONS_KEY = "SETTINGS_ALLOW_INTEGRATIONS_KEY"
const val SETTINGS_INTEGRATION_MANAGER_UI_URL_KEY = "SETTINGS_INTEGRATION_MANAGER_UI_URL_KEY"
const val SETTINGS_SECURE_MESSAGE_RECOVERY_PREFERENCE_KEY = "SETTINGS_SECURE_MESSAGE_RECOVERY_PREFERENCE_KEY"
const val SETTINGS_PERSISTED_SPACE_BACKSTACK = "SETTINGS_PERSISTED_SPACE_BACKSTACK"

const val SETTINGS_CRYPTOGRAPHY_HS_ADMIN_DISABLED_E2E_DEFAULT = "SETTINGS_CRYPTOGRAPHY_HS_ADMIN_DISABLED_E2E_DEFAULT"
// const val SETTINGS_SECURE_BACKUP_RESET_PREFERENCE_KEY = "SETTINGS_SECURE_BACKUP_RESET_PREFERENCE_KEY"
Expand Down Expand Up @@ -1126,6 +1127,25 @@ class VectorPreferences @Inject constructor(
.apply()
}

/**
* Sets the space backstack that is used for up navigation
* This needs to be persisted because navigating up through spaces should work across sessions
*
* Only the IDs of the spaces are stored
*/
fun setPersistedSpaceBackstack(spaceBackstack: List<String?>) {
val spaceIdsJoined = spaceBackstack.takeIf { it.isNotEmpty() }?.joinToString(",")
defaultPrefs.edit().putString(SETTINGS_PERSISTED_SPACE_BACKSTACK, spaceIdsJoined).apply()
}

/**
* Gets the space backstack used for up navigation
*/
fun getPersistedSpaceBackstack(): List<String?> {
val spaceIdsJoined = defaultPrefs.getString(SETTINGS_PERSISTED_SPACE_BACKSTACK, null)
return spaceIdsJoined?.takeIf { it.isNotEmpty() }?.split(",").orEmpty()
}

fun showLiveSenderInfo(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_TIMELINE_SHOW_LIVE_SENDER_INFO, getDefault(R.bool.settings_timeline_show_live_sender_info_default))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,41 @@

package im.vector.app.features.spaces

import android.content.Context
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel

@EpoxyModelClass
abstract class NewSpaceListHeaderItem : VectorEpoxyModel<NewSpaceListHeaderItem.Holder>(R.layout.item_new_space_list_header) {
class Holder : VectorEpoxyHolder()

@EpoxyAttribute var currentSpace: String? = null
@EpoxyAttribute var spaceHistory: List<Pair<String?, String>> = emptyList()

override fun bind(holder: Holder) {
super.bind(holder)
holder.spaceHeader.text = buildSpaceHeaderText(holder.spaceHeader.context)
}

private fun buildSpaceHeaderText(context: Context): String {
val allChats = context.getString(R.string.all_chats)
var spaceHeaderText = allChats

val nonRootSpaceHistory = spaceHistory.filter { it.second.isNotEmpty() }

if (nonRootSpaceHistory.isNotEmpty()) {
spaceHeaderText += " > ${nonRootSpaceHistory.joinToString(" > ") { it.second }}"
}
if (currentSpace != null) {
spaceHeaderText += " > $currentSpace"
}
return spaceHeaderText
}

class Holder : VectorEpoxyHolder() {
val spaceHeader by bind<TextView>(R.id.space_header)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,24 @@ class NewSpaceSummaryController @Inject constructor(
nonNullViewState.spaces,
nonNullViewState.selectedSpace,
nonNullViewState.rootSpacesOrdered,
nonNullViewState.homeAggregateCount
nonNullViewState.homeAggregateCount,
nonNullViewState.spaceHistory,
)
}

private fun buildGroupModels(
spaceSummaries: List<RoomSummary>?,
selectedSpace: RoomSummary?,
rootSpaces: List<RoomSummary>?,
homeCount: RoomAggregateNotificationCount
homeCount: RoomAggregateNotificationCount,
spaceHistory: List<Pair<String?, String>>,
) {
val host = this

newSpaceListHeaderItem {
id("space_list_header")
currentSpace(selectedSpace?.displayName)
spaceHistory(spaceHistory)
}

if (selectedSpace != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class SpaceListViewModel @AssistedInject constructor(
private val session: Session,
private val vectorPreferences: VectorPreferences,
private val autoAcceptInvites: AutoAcceptInvites,
private val analyticsTracker: AnalyticsTracker
private val analyticsTracker: AnalyticsTracker,
) : VectorViewModel<SpaceListViewState, SpaceListAction, SpaceListViewEvents>(initialState) {

@AssistedFactory
Expand All @@ -85,11 +85,14 @@ class SpaceListViewModel @AssistedInject constructor(
}

observeSpaceSummaries()
val spaceHistory = spaceStateHandler.getPersistedSpaceBackstack()
.map { it to it?.let { session.roomService().getRoomSummary(it)?.displayName }.orEmpty() }
spaceStateHandler.getSelectedSpaceFlow()
.distinctUntilChanged()
.setOnEach { selectedSpaceOption ->
copy(
selectedSpace = selectedSpaceOption.orNull()
selectedSpace = selectedSpaceOption.orNull(),
spaceHistory = spaceHistory,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ data class SpaceListViewState(
val spaceOrderInfo: Map<String, String?>? = null,
val spaceOrderLocalEchos: Map<String, String?>? = null,
val expandedStates: Map<String, Boolean> = emptyMap(),
val spaceHistory: List<Pair<String?, String>> = emptyList(), // List of space id to display name
val homeAggregateCount: RoomAggregateNotificationCount = RoomAggregateNotificationCount(0, 0)
) : MavericksState
1 change: 1 addition & 0 deletions vector/src/main/res/layout/item_new_space_list_header.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/space_header"
style="@style/TextAppearance.Vector.Body.Medium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
Expand Down

0 comments on commit 86382d0

Please sign in to comment.