@ -17,11 +17,8 @@ import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavDirections
import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.fragment_history.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
@ -34,18 +31,17 @@ import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.addons.showSnackBar
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.components.history.createSynchronousPagedHistoryProvider
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getStringWithArgSafe
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.ext.toShortUrl
import org.mozilla.fenix.library.LibraryPageFragment
import org.mozilla.fenix.utils.allowUndo
@SuppressWarnings ( " TooManyFunctions " , " LargeClass " )
class HistoryFragment : LibraryPageFragment < HistoryItem > ( ) , UserInteractionHandler {
@ -53,8 +49,6 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
private lateinit var historyView : HistoryView
private lateinit var historyInteractor : HistoryInteractor
private lateinit var viewModel : HistoryViewModel
private var undoScope : CoroutineScope ? = null
private var pendingHistoryDeletionJob : ( suspend ( ) -> Unit ) ? = null
override fun onCreateView (
inflater : LayoutInflater ,
@ -65,10 +59,7 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
historyStore = StoreProvider . get ( this ) {
HistoryFragmentStore (
HistoryFragmentState (
items = listOf ( ) ,
mode = HistoryFragmentState . Mode . Normal ,
pendingDeletionIds = emptySet ( ) ,
isDeletingItems = false
items = listOf ( ) , mode = HistoryFragmentState . Mode . Normal
)
)
}
@ -120,18 +111,18 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
}
private fun deleteHistoryItems ( items : Set < HistoryItem > ) {
updatePendingHistoryToDelete( items )
undoScope = CoroutineScope ( IO )
undoScope ?. allowUndo (
requireView ( ) ,
getMultiSelectSnackBarMessage ( items ) ,
getString ( R . string . bookmark _undo _deletion ) ,
{
undoPendingDeletion ( items )
} ,
getDeleteHistoryItemsOperation( items )
)
val message = getMultiSelectSnackBarMessage ( items )
viewLifecycleOwner. lifecycleScope . launch {
context ?. components ?. run {
for ( item in items ) {
analytics . metrics . track ( Event . HistoryItemRemoved )
core . historyStorage . deleteVisit ( item . url , item . visitedAt )
}
}
viewModel . invalidate ( )
showSnackBar ( requireView ( ) , message )
historyStore. dispatch ( HistoryFragmentAction . ExitDeletionMode )
}
}
@ExperimentalCoroutinesApi
@ -155,8 +146,8 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
override fun onCreateOptionsMenu ( menu : Menu , inflater : MenuInflater ) {
val menuRes = when ( historyStore . state . mode ) {
HistoryFragmentState . Mode . Normal -> R . menu . library _menu
is HistoryFragmentState . Mode . Syncing -> R . menu . library _menu
is HistoryFragmentState . Mode . Editing -> R . menu . history _select _multi
else -> return
}
inflater . inflate ( menuRes , menu )
@ -171,8 +162,13 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
true
}
R . id . delete _history _multi _select -> {
deleteHistoryItems ( historyStore . state . mode . selectedItems )
historyStore . dispatch ( HistoryFragmentAction . ExitEditMode )
val message = getMultiSelectSnackBarMessage ( selectedItems )
viewLifecycleOwner . lifecycleScope . launch ( Main ) {
deleteSelectedHistory ( historyStore . state . mode . selectedItems , requireComponents )
viewModel . invalidate ( )
historyStore . dispatch ( HistoryFragmentAction . ExitDeletionMode )
showSnackBar ( requireView ( ) , message )
}
true
}
R . id . open _history _in _new _tabs _multi _select -> {
@ -181,7 +177,10 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
selectedItem . url
}
navigate ( HistoryFragmentDirections . actionGlobalHome ( ) )
nav (
R . id . historyFragment ,
HistoryFragmentDirections . actionGlobalHome ( )
)
true
}
R . id . open _history _in _private _tabs _multi _select -> {
@ -194,7 +193,10 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
browsingModeManager . mode = BrowsingMode . Private
supportActionBar ?. hide ( )
}
navigate ( HistoryFragmentDirections . actionGlobalHome ( ) )
nav (
R . id . historyFragment ,
HistoryFragmentDirections . actionGlobalHome ( )
)
true
}
else -> super . onOptionsItemSelected ( item )
@ -204,22 +206,14 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
return if ( historyItems . size > 1 ) {
getString ( R . string . history _delete _multiple _items _snackbar )
} else {
requireContext( ) . getStringWithArgSafe (
getString(
R . string . history _delete _single _item _snackbar ,
historyItems . first ( ) . url . toShortUrl ( requireComponents . publicSuffixList )
)
}
}
override fun onPause ( ) {
invokePendingDeletion ( )
super . onPause ( )
}
override fun onBackPressed ( ) : Boolean {
invokePendingDeletion ( )
return historyView . onBackPressed ( )
}
override fun onBackPressed ( ) : Boolean = historyView . onBackPressed ( )
private fun openItem ( item : HistoryItem , mode : BrowsingMode ? = null ) {
requireComponents . analytics . metrics . track ( Event . HistoryItemOpened )
@ -259,58 +253,23 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
}
}
private suspend fun deleteSelectedHistory (
selected : Set < HistoryItem > ,
components : Components = requireComponents
) {
requireComponents . analytics . metrics . track ( Event . HistoryItemRemoved )
val storage = components . core . historyStorage
for ( item in selected ) {
storage . deleteVisit ( item . url , item . visitedAt )
}
}
private fun share ( data : List < ShareData > ) {
requireComponents . analytics . metrics . track ( Event . HistoryItemShared )
val directions = HistoryFragmentDirections . actionGlobalShareFragment (
data = data . toTypedArray ( )
)
navigate ( directions )
}
private fun navigate ( directions : NavDirections ) {
invokePendingDeletion ( )
findNavController ( ) . nav (
R . id . historyFragment ,
directions
)
}
private fun getDeleteHistoryItemsOperation ( items : Set < HistoryItem > ) : ( suspend ( ) -> Unit ) {
return {
CoroutineScope ( IO ) . launch {
historyStore . dispatch ( HistoryFragmentAction . EnterDeletionMode )
context ?. components ?. run {
for ( item in items ) {
analytics . metrics . track ( Event . HistoryItemRemoved )
core . historyStorage . deleteVisit ( item . url , item . visitedAt )
}
}
historyStore . dispatch ( HistoryFragmentAction . ExitDeletionMode )
pendingHistoryDeletionJob = null
}
}
}
private fun updatePendingHistoryToDelete ( items : Set < HistoryItem > ) {
pendingHistoryDeletionJob = getDeleteHistoryItemsOperation ( items )
val ids = items . map { item -> item . visitedAt } . toSet ( )
historyStore . dispatch ( HistoryFragmentAction . AddPendingDeletionSet ( ids ) )
}
private fun undoPendingDeletion ( items : Set < HistoryItem > ) {
pendingHistoryDeletionJob = null
val ids = items . map { item -> item . visitedAt } . toSet ( )
historyStore . dispatch ( HistoryFragmentAction . UndoRemovePendingDeletionSet ( ids ) )
}
private fun invokePendingDeletion ( ) {
pendingHistoryDeletionJob ?. let {
viewLifecycleOwner . lifecycleScope . launch {
it . invoke ( )
} . invokeOnCompletion {
pendingHistoryDeletionJob = null
}
}
nav ( R . id . historyFragment , directions )
}
private suspend fun syncHistory ( ) {