1
0
Fork 0

For #12831 - Disable SwipeRefreshLayout while swiping a bookmark.

master
Kainalu Hagiwara 2020-07-28 16:57:43 -07:00
parent 5318d64911
commit 30c7b5ea5e
8 changed files with 215 additions and 45 deletions

View File

@ -45,6 +45,8 @@ interface BookmarkController {
fun handleBookmarkFolderDeletion(nodes: Set<BookmarkNode>)
fun handleRequestSync()
fun handleBackPressed()
fun handleStartSwipingItem()
fun handleStopSwipingItem()
}
@Suppress("TooManyFunctions")
@ -169,6 +171,14 @@ class DefaultBookmarkController(
}
}
override fun handleStartSwipingItem() {
store.dispatch(BookmarkFragmentAction.SwipeRefreshAvailabilityChanged(false))
}
override fun handleStopSwipingItem() {
store.dispatch(BookmarkFragmentAction.SwipeRefreshAvailabilityChanged(true))
}
private fun openInNewTab(
searchTermOrURL: String,
newTab: Boolean,

View File

@ -120,4 +120,12 @@ class BookmarkFragmentInteractor(
override fun onRequestSync() {
bookmarksController.handleRequestSync()
}
override fun onStartSwipingItem() {
bookmarksController.handleStartSwipingItem()
}
override fun onStopSwipingItem() {
bookmarksController.handleStopSwipingItem()
}
}

View File

@ -23,12 +23,14 @@ class BookmarkFragmentStore(
* @property guidBackstack A set of guids for bookmark nodes we have visited. Used to traverse back
* up the tree after a sync.
* @property isLoading true if bookmarks are still being loaded from disk
* @property isSwipeToRefreshEnabled true if swipe to refresh should be enabled
*/
data class BookmarkFragmentState(
val tree: BookmarkNode?,
val mode: Mode = Mode.Normal(),
val guidBackstack: List<String> = emptyList(),
val isLoading: Boolean = true
val isLoading: Boolean = true,
val isSwipeToRefreshEnabled: Boolean = true
) : State {
sealed class Mode {
open val selectedItems = emptySet<BookmarkNode>()
@ -49,6 +51,7 @@ sealed class BookmarkFragmentAction : Action {
object DeselectAll : BookmarkFragmentAction()
object StartSync : BookmarkFragmentAction()
object FinishSync : BookmarkFragmentAction()
data class SwipeRefreshAvailabilityChanged(val enabled: Boolean) : BookmarkFragmentAction()
}
/**
@ -71,31 +74,37 @@ private fun bookmarkFragmentStateReducer(
} + action.tree.guid
val items = state.mode.selectedItems.filter { it in action.tree }
val mode = when {
state.mode is BookmarkFragmentState.Mode.Syncing -> {
BookmarkFragmentState.Mode.Syncing
}
items.isEmpty() -> {
BookmarkFragmentState.Mode.Normal(shouldShowMenu(action.tree.guid))
}
else -> BookmarkFragmentState.Mode.Selecting(items.toSet())
}
state.copy(
tree = action.tree,
mode = when {
state.mode is BookmarkFragmentState.Mode.Syncing -> {
BookmarkFragmentState.Mode.Syncing
}
items.isEmpty() -> {
BookmarkFragmentState.Mode.Normal(shouldShowMenu(action.tree.guid))
}
else -> BookmarkFragmentState.Mode.Selecting(items.toSet())
},
mode = mode,
guidBackstack = backstack,
isLoading = false
isLoading = false,
isSwipeToRefreshEnabled = mode !is BookmarkFragmentState.Mode.Selecting
)
}
is BookmarkFragmentAction.Select ->
state.copy(mode = BookmarkFragmentState.Mode.Selecting(state.mode.selectedItems + action.item))
is BookmarkFragmentAction.Select -> state.copy(
mode = BookmarkFragmentState.Mode.Selecting(state.mode.selectedItems + action.item),
isSwipeToRefreshEnabled = false
)
is BookmarkFragmentAction.Deselect -> {
val items = state.mode.selectedItems - action.item
val mode = if (items.isEmpty()) {
BookmarkFragmentState.Mode.Normal()
} else {
BookmarkFragmentState.Mode.Selecting(items)
}
state.copy(
mode = if (items.isEmpty()) {
BookmarkFragmentState.Mode.Normal()
} else {
BookmarkFragmentState.Mode.Selecting(items)
}
mode = mode,
isSwipeToRefreshEnabled = mode !is BookmarkFragmentState.Mode.Selecting
)
}
is BookmarkFragmentAction.DeselectAll ->
@ -104,18 +113,22 @@ private fun bookmarkFragmentStateReducer(
BookmarkFragmentState.Mode.Syncing
} else {
BookmarkFragmentState.Mode.Normal()
}
)
is BookmarkFragmentAction.StartSync ->
state.copy(
mode = BookmarkFragmentState.Mode.Syncing
)
is BookmarkFragmentAction.FinishSync ->
state.copy(
mode = BookmarkFragmentState.Mode.Normal(
showMenu = shouldShowMenu(state.tree?.guid)
)
},
isSwipeToRefreshEnabled = true
)
is BookmarkFragmentAction.StartSync -> state.copy(
mode = BookmarkFragmentState.Mode.Syncing,
isSwipeToRefreshEnabled = true
)
is BookmarkFragmentAction.FinishSync -> state.copy(
mode = BookmarkFragmentState.Mode.Normal(
showMenu = shouldShowMenu(state.tree?.guid)
),
isSwipeToRefreshEnabled = true
)
is BookmarkFragmentAction.SwipeRefreshAvailabilityChanged -> state.copy(
isSwipeToRefreshEnabled = action.enabled && state.mode !is BookmarkFragmentState.Mode.Selecting
)
}
}

View File

@ -116,6 +116,15 @@ class BookmarkTouchCallback(
target: RecyclerView.ViewHolder
): Boolean = false
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
interactor.onStartSwipingItem()
} else {
interactor.onStopSwipingItem()
}
}
private fun setBounds(
background: Drawable,
backgroundBounds: Rect,

View File

@ -98,6 +98,16 @@ interface BookmarkViewInteractor : SelectionInteractor<BookmarkNode> {
*
*/
fun onRequestSync()
/**
* Handles the start of a swipe on a bookmark.
*/
fun onStartSwipingItem()
/**
* Handles the end of a swipe on a bookmark.
*/
fun onStopSwipingItem()
}
class BookmarkView(
@ -152,8 +162,7 @@ class BookmarkView(
}
}
view.bookmarks_progress_bar.isVisible = state.isLoading
view.swipe_refresh.isEnabled =
state.mode is BookmarkFragmentState.Mode.Normal || state.mode is BookmarkFragmentState.Mode.Syncing
view.swipe_refresh.isEnabled = state.isSwipeToRefreshEnabled
view.swipe_refresh.isRefreshing = state.mode is BookmarkFragmentState.Mode.Syncing
}

View File

@ -337,4 +337,22 @@ class BookmarkControllerTest {
navController.popBackStack()
}
}
@Test
fun `handleStartSwipingItem disables swipe to refresh`() {
controller.handleStartSwipingItem()
verify {
bookmarkStore.dispatch(BookmarkFragmentAction.SwipeRefreshAvailabilityChanged(false))
}
}
@Test
fun `handleStopSwipingItem attempts to enable swipe to refresh`() {
controller.handleStopSwipingItem()
verify {
bookmarkStore.dispatch(BookmarkFragmentAction.SwipeRefreshAvailabilityChanged(true))
}
}
}

View File

@ -210,4 +210,22 @@ class BookmarkFragmentInteractorTest {
bookmarkController.handleRequestSync()
}
}
@Test
fun `start swiping an item`() {
interactor.onStartSwipingItem()
verify {
bookmarkController.handleStartSwipingItem()
}
}
@Test
fun `stop swiping an item`() {
interactor.onStopSwipingItem()
verify {
bookmarkController.handleStopSwipingItem()
}
}
}

View File

@ -80,32 +80,63 @@ class BookmarkFragmentStoreTest {
@Test
fun `ensure selected items remain selected after a tree change`() = runBlocking {
val initialState = BookmarkFragmentState(tree, BookmarkFragmentState.Mode.Selecting(setOf(item, subfolder)))
val initialState = BookmarkFragmentState(
tree,
BookmarkFragmentState.Mode.Selecting(setOf(item, subfolder)),
isLoading = false,
isSwipeToRefreshEnabled = false
)
val store = BookmarkFragmentStore(initialState)
store.dispatch(BookmarkFragmentAction.Change(newTree)).join()
assertEquals(store.state.tree, newTree)
assertEquals(store.state.mode, BookmarkFragmentState.Mode.Selecting(setOf(subfolder)))
assertEquals(
store.state,
BookmarkFragmentState(
newTree,
BookmarkFragmentState.Mode.Selecting(setOf(subfolder)),
guidBackstack = listOf(tree.guid),
isLoading = false,
isSwipeToRefreshEnabled = false
)
)
}
@Test
fun `select and deselect bookmarks changes the mode`() = runBlocking {
fun `select and deselect a single bookmark changes the mode and swipe to refresh state`() = runBlocking {
val initialState = BookmarkFragmentState(tree)
val store = BookmarkFragmentStore(initialState)
store.dispatch(BookmarkFragmentAction.Select(childItem)).join()
assertEquals(store.state, BookmarkFragmentState(tree, BookmarkFragmentState.Mode.Selecting(setOf(childItem))))
assertEquals(
store.state,
BookmarkFragmentState(
tree,
BookmarkFragmentState.Mode.Selecting(setOf(childItem)),
isSwipeToRefreshEnabled = false
)
)
store.dispatch(BookmarkFragmentAction.Deselect(childItem)).join()
assertEquals(store.state, BookmarkFragmentState(tree, BookmarkFragmentState.Mode.Normal()))
assertEquals(
store.state,
BookmarkFragmentState(
tree,
BookmarkFragmentState.Mode.Normal(),
isSwipeToRefreshEnabled = true
)
)
}
@Test
fun `selecting the same item twice does nothing`() = runBlocking {
val initialState = BookmarkFragmentState(tree, BookmarkFragmentState.Mode.Selecting(setOf(item, subfolder)))
val initialState = BookmarkFragmentState(
tree,
BookmarkFragmentState.Mode.Selecting(setOf(item, subfolder)),
isSwipeToRefreshEnabled = false
)
val store = BookmarkFragmentStore(initialState)
store.dispatch(BookmarkFragmentAction.Select(item)).join()
@ -115,7 +146,11 @@ class BookmarkFragmentStoreTest {
@Test
fun `deselecting an unselected bookmark does nothing`() = runBlocking {
val initialState = BookmarkFragmentState(tree, BookmarkFragmentState.Mode.Selecting(setOf(childItem)))
val initialState = BookmarkFragmentState(
tree,
BookmarkFragmentState.Mode.Selecting(setOf(childItem)),
isSwipeToRefreshEnabled = false
)
val store = BookmarkFragmentStore(initialState)
store.dispatch(BookmarkFragmentAction.Deselect(item)).join()
@ -134,14 +169,25 @@ class BookmarkFragmentStoreTest {
}
@Test
fun `deselect all bookmarks changes the mode`() = runBlocking {
val initialState = BookmarkFragmentState(tree, BookmarkFragmentState.Mode.Selecting(setOf(item, childItem)))
val store = BookmarkFragmentStore(initialState)
fun `deselect all bookmarks changes the mode and updates swipe to refresh state`() =
runBlocking {
val initialState = BookmarkFragmentState(
tree,
BookmarkFragmentState.Mode.Selecting(setOf(item, childItem)),
isSwipeToRefreshEnabled = false
)
val store = BookmarkFragmentStore(initialState)
store.dispatch(BookmarkFragmentAction.DeselectAll).join()
store.dispatch(BookmarkFragmentAction.DeselectAll).join()
assertEquals(store.state, initialState.copy(mode = BookmarkFragmentState.Mode.Normal()))
}
assertEquals(
store.state,
initialState.copy(
mode = BookmarkFragmentState.Mode.Normal(),
isSwipeToRefreshEnabled = true
)
)
}
@Test
fun `deselect all bookmarks when none are selected`() = runBlocking {
@ -214,6 +260,45 @@ class BookmarkFragmentStoreTest {
assertEquals(BookmarkFragmentState.Mode.Syncing, store.state.mode)
}
@Test
fun `enabling swipe to refresh in Normal mode works`() = runBlocking {
val initialState = BookmarkFragmentState(
tree,
BookmarkFragmentState.Mode.Normal(),
isSwipeToRefreshEnabled = false
)
val store = BookmarkFragmentStore(initialState)
store.dispatch(BookmarkFragmentAction.SwipeRefreshAvailabilityChanged(true)).join()
assertEquals(true, store.state.isSwipeToRefreshEnabled)
}
@Test
fun `enabling swipe to refresh in Syncing mode works`() = runBlocking {
val initialState = BookmarkFragmentState(
tree,
BookmarkFragmentState.Mode.Syncing,
isSwipeToRefreshEnabled = false
)
val store = BookmarkFragmentStore(initialState)
store.dispatch(BookmarkFragmentAction.SwipeRefreshAvailabilityChanged(true)).join()
assertEquals(true, store.state.isSwipeToRefreshEnabled)
}
@Test
fun `enabling swipe to refresh in Selecting mode does not work`() = runBlocking {
val initialState = BookmarkFragmentState(
tree,
BookmarkFragmentState.Mode.Selecting(emptySet()),
isSwipeToRefreshEnabled = false
)
val store = BookmarkFragmentStore(initialState)
store.dispatch(BookmarkFragmentAction.SwipeRefreshAvailabilityChanged(true)).join()
assertEquals(false, store.state.isSwipeToRefreshEnabled)
}
private val item = BookmarkNode(BookmarkNodeType.ITEM, "456", "123", 0, "Mozilla", "http://mozilla.org", null)
private val separator = BookmarkNode(BookmarkNodeType.SEPARATOR, "789", "123", 1, null, null, null)
private val subfolder = BookmarkNode(BookmarkNodeType.FOLDER, "987", "123", 0, "Subfolder", null, listOf())