Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

Commit

Permalink
For #2165 - Implement pull-to-refresh gesture to sync history.
Browse files Browse the repository at this point in the history
  • Loading branch information
person808 committed Jun 10, 2020
1 parent 1a19b06 commit 8587153
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface HistoryController {
fun handleDeleteSome(items: Set<HistoryItem>)
fun handleCopyUrl(item: HistoryItem)
fun handleShare(item: HistoryItem)
fun handleRequestSync()
}

class DefaultHistoryController(
Expand All @@ -36,13 +37,17 @@ class DefaultHistoryController(
private val openToBrowser: (item: HistoryItem, mode: BrowsingMode?) -> Unit,
private val displayDeleteAll: () -> Unit,
private val invalidateOptionsMenu: () -> Unit,
private val deleteHistoryItems: (Set<HistoryItem>) -> Unit
private val deleteHistoryItems: (Set<HistoryItem>) -> Unit,
private val syncHistory: (() -> Unit) -> Unit
) : HistoryController {
override fun handleOpen(item: HistoryItem, mode: BrowsingMode?) {
openToBrowser(item, mode)
}

override fun handleSelect(item: HistoryItem) {
if (store.state.mode === HistoryFragmentState.Mode.Syncing) {
return
}
store.dispatch(HistoryFragmentAction.AddItemForRemoval(item))
}

Expand Down Expand Up @@ -87,4 +92,11 @@ class DefaultHistoryController(
)
)
}

override fun handleRequestSync() {
store.dispatch(HistoryFragmentAction.StartSync)
syncHistory.invoke {
store.dispatch(HistoryFragmentAction.FinishSync)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import mozilla.components.concept.engine.prompt.ShareData
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.service.fxa.sync.SyncReason
import mozilla.components.support.base.feature.UserInteractionHandler
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
Expand Down Expand Up @@ -75,7 +76,8 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
::openItem,
::displayDeleteAllDialog,
::invalidateOptionsMenu,
::deleteHistoryItems
::deleteHistoryItems,
::syncHistory
)
historyInteractor = HistoryInteractor(
historyController
Expand Down Expand Up @@ -268,4 +270,13 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
)
nav(R.id.historyFragment, directions)
}

private fun syncHistory(onSyncFinished: () -> Unit) {
viewLifecycleOwner.lifecycleScope.launch {
val accountManager = requireComponents.backgroundServices.accountManager
accountManager.syncNowAsync(SyncReason.User).await()
viewModel.invalidate()
onSyncFinished.invoke()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ sealed class HistoryFragmentAction : Action {
data class RemoveItemForRemoval(val item: HistoryItem) : HistoryFragmentAction()
object EnterDeletionMode : HistoryFragmentAction()
object ExitDeletionMode : HistoryFragmentAction()
object StartSync : HistoryFragmentAction()
object FinishSync : HistoryFragmentAction()
}

/**
Expand All @@ -45,6 +47,7 @@ data class HistoryFragmentState(val items: List<HistoryItem>, val mode: Mode) :

object Normal : Mode()
object Deleting : Mode()
object Syncing : Mode()
data class Editing(override val selectedItems: Set<HistoryItem>) : Mode()
}
}
Expand Down Expand Up @@ -72,5 +75,7 @@ private fun historyStateReducer(
is HistoryFragmentAction.ExitEditMode -> state.copy(mode = HistoryFragmentState.Mode.Normal)
is HistoryFragmentAction.EnterDeletionMode -> state.copy(mode = HistoryFragmentState.Mode.Deleting)
is HistoryFragmentAction.ExitDeletionMode -> state.copy(mode = HistoryFragmentState.Mode.Normal)
is HistoryFragmentAction.StartSync -> state.copy(mode = HistoryFragmentState.Mode.Syncing)
is HistoryFragmentAction.FinishSync -> state.copy(mode = HistoryFragmentState.Mode.Normal)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,8 @@ class HistoryInteractor(
override fun onDeleteSome(items: Set<HistoryItem>) {
historyController.handleDeleteSome(items)
}

override fun onRequestSync() {
historyController.handleRequestSync()
}
}
17 changes: 17 additions & 0 deletions app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import mozilla.components.support.base.feature.UserInteractionHandler
import org.mozilla.fenix.R
import org.mozilla.fenix.library.LibraryPageView
import org.mozilla.fenix.library.SelectionInteractor
import org.mozilla.fenix.theme.ThemeManager

/**
* Interface for the HistoryViewInteractor. This interface is implemented by objects that want
Expand Down Expand Up @@ -71,6 +72,11 @@ interface HistoryViewInteractor : SelectionInteractor<HistoryItem> {
* @param items the history items to delete
*/
fun onDeleteSome(items: Set<HistoryItem>)

/**
* Called when the user requests a sync of the history
*/
fun onRequestSync()
}

/**
Expand All @@ -97,12 +103,23 @@ class HistoryView(
adapter = historyAdapter
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
}

val primaryTextColor =
ThemeManager.resolveAttribute(R.attr.primaryText, context)
view.swipe_refresh.setColorSchemeColors(primaryTextColor)
view.swipe_refresh.setOnRefreshListener {
interactor.onRequestSync()
view.history_list.scrollToPosition(0)
}
}

fun update(state: HistoryFragmentState) {
val oldMode = mode

view.progress_bar.isVisible = state.mode === HistoryFragmentState.Mode.Deleting
view.swipe_refresh.isRefreshing = state.mode === HistoryFragmentState.Mode.Syncing
view.swipe_refresh.isEnabled =
state.mode === HistoryFragmentState.Mode.Normal || state.mode === HistoryFragmentState.Mode.Syncing
items = state.items
mode = state.mode

Expand Down
9 changes: 8 additions & 1 deletion app/src/main/res/layout/component_history.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,16 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.recyclerview.widget.RecyclerView

<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/history_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/history_list_item"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class HistoryControllerTest {
private val displayDeleteAll: () -> Unit = mockk(relaxed = true)
private val invalidateOptionsMenu: () -> Unit = mockk(relaxed = true)
private val deleteHistoryItems: (Set<HistoryItem>) -> Unit = mockk(relaxed = true)
private val syncHistory: (() -> Unit) -> Unit = mockk(relaxed = true)
private val controller = DefaultHistoryController(
store,
navController,
Expand All @@ -47,7 +48,8 @@ class HistoryControllerTest {
openInBrowser,
displayDeleteAll,
invalidateOptionsMenu,
deleteHistoryItems
deleteHistoryItems,
syncHistory
)

@Before
Expand Down Expand Up @@ -104,6 +106,17 @@ class HistoryControllerTest {
}
}

@Test
fun onSelectHistoryItemDuringSync() {
every { state.mode } returns HistoryFragmentState.Mode.Syncing

controller.handleSelect(historyItem)

verify(exactly = 0) {
store.dispatch(HistoryFragmentAction.AddItemForRemoval(historyItem))
}
}

@Test
fun onBackPressedInNormalMode() {
every { state.mode } returns HistoryFragmentState.Mode.Normal
Expand Down Expand Up @@ -189,4 +202,13 @@ class HistoryControllerTest {
assertEquals(historyItem.title, (directions.captured.arguments["data"] as Array<ShareData>)[0].title)
assertEquals(historyItem.url, (directions.captured.arguments["data"] as Array<ShareData>)[0].url)
}

@Test
fun onRequestSync() {
controller.handleRequestSync()

verify {
syncHistory.invoke(any())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,29 @@ class HistoryFragmentStoreTest {
assertEquals(store.state.mode, HistoryFragmentState.Mode.Editing(setOf(historyItem)))
}

@Test
fun startSync() = runBlocking {
val initialState = emptyDefaultState()
val store = HistoryFragmentStore(initialState)

store.dispatch(HistoryFragmentAction.StartSync).join()
assertNotSame(initialState, store.state)
assertEquals(HistoryFragmentState.Mode.Syncing, store.state.mode)
}

@Test
fun finishSync() = runBlocking {
val initialState = HistoryFragmentState(
items = listOf(),
mode = HistoryFragmentState.Mode.Syncing
)
val store = HistoryFragmentStore(initialState)

store.dispatch(HistoryFragmentAction.FinishSync).join()
assertNotSame(initialState, store.state)
assertEquals(HistoryFragmentState.Mode.Normal, store.state.mode)
}

private fun emptyDefaultState(): HistoryFragmentState = HistoryFragmentState(
items = listOf(),
mode = HistoryFragmentState.Mode.Normal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,12 @@ class HistoryInteractorTest {
controller.handleDeleteSome(items)
}
}

@Test
fun onRequestSync() {
interactor.onRequestSync()
verifyAll {
controller.handleRequestSync()
}
}
}

0 comments on commit 8587153

Please sign in to comment.