1
0
Fork 0

For #356 - Gives a user the ability to delete their history

master
Jeff Boek 2019-03-29 09:46:34 -07:00
parent 6491adf029
commit 00ad9d3f6f
6 changed files with 103 additions and 91 deletions

View File

@ -13,8 +13,6 @@ import android.widget.CompoundButton
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observer
import org.mozilla.fenix.R
import androidx.core.content.ContextCompat
import kotlinx.android.synthetic.main.history_delete.view.*
import kotlinx.android.synthetic.main.history_header.view.*
import kotlinx.android.synthetic.main.history_list_item.view.*
import mozilla.components.browser.menu.BrowserMenu
@ -226,48 +224,6 @@ class HistoryAdapter(
}
}
class HistoryDeleteViewHolder(
view: View,
private val actionEmitter: Observer<HistoryAction>
) : RecyclerView.ViewHolder(view) {
private lateinit var mode: HistoryState.Mode
private val button = view.delete_history_button.apply {
setOnClickListener {
val mode = mode
if (mode is HistoryState.Mode.Editing && mode.selectedItems.isNotEmpty()) {
actionEmitter.onNext(HistoryAction.Delete.Some(mode.selectedItems))
} else {
actionEmitter.onNext(HistoryAction.Delete.All)
}
}
}
private val text = view.delete_history_button_text.apply {
val color = ContextCompat.getColor(context, R.color.photonRed60)
val drawable = ContextCompat.getDrawable(context, R.drawable.ic_delete)
drawable?.setTint(color)
this.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null)
}
fun bind(mode: HistoryState.Mode) {
this.mode = mode
val text = if (mode is HistoryState.Mode.Editing && mode.selectedItems.isNotEmpty()) {
text.context.resources.getString(R.string.history_delete_some, mode.selectedItems.size)
} else {
text.context.resources.getString(R.string.history_delete_all)
}
button.contentDescription = text
this.text.text = text
}
companion object {
const val LAYOUT_ID = R.layout.history_delete
}
}
private var historyList: HistoryList = HistoryList(emptyList())
private var mode: HistoryState.Mode = HistoryState.Mode.Normal

View File

@ -39,7 +39,7 @@ class HistoryComponent(
override val reducer: (HistoryState, HistoryChange) -> HistoryState = { state, change ->
when (change) {
is HistoryChange.Change -> state.copy(items = change.list)
is HistoryChange.Change -> state.copy(mode = HistoryState.Mode.Normal, items = change.list)
is HistoryChange.EnterEditMode -> state.copy(mode = HistoryState.Mode.Editing(listOf(change.item)))
is HistoryChange.AddItemForRemoval -> {
val mode = state.mode

View File

@ -17,9 +17,10 @@ import androidx.navigation.NavOptions
import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.fragment_history.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.coroutineScope
import mozilla.components.support.base.feature.BackHandler
import org.mozilla.fenix.utils.ItsNotBrokenSnack
import org.mozilla.fenix.R
@ -29,6 +30,7 @@ import org.mozilla.fenix.mvi.getAutoDisposeObservable
import org.mozilla.fenix.mvi.getManagedEmitter
import kotlin.coroutines.CoroutineContext
@SuppressWarnings("TooManyFunctions")
class HistoryFragment : Fragment(), CoroutineScope, BackHandler {
private lateinit var job: Job
@ -81,18 +83,13 @@ class HistoryFragment : Fragment(), CoroutineScope, BackHandler {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
launch(Dispatchers.IO) {
val items = requireComponents.core.historyStorage.getDetailedVisits(0)
.asReversed()
.mapIndexed { id, item -> HistoryItem(id, item.url, item.visitTime) }
launch(Dispatchers.Main) {
getManagedEmitter<HistoryChange>().onNext(HistoryChange.Change(items))
}
reloadData()
}
}
// This method triggers the complexity warning. However it's actually not that hard to understand.
@SuppressWarnings("ComplexMethod")
override fun onStart() {
super.onStart()
getAutoDisposeObservable<HistoryAction>()
@ -107,6 +104,20 @@ class HistoryFragment : Fragment(), CoroutineScope, BackHandler {
.onNext(HistoryChange.RemoveItemForRemoval(it.item))
is HistoryAction.BackPressed -> getManagedEmitter<HistoryChange>()
.onNext(HistoryChange.ExitEditMode)
is HistoryAction.Delete.All -> launch(Dispatchers.IO) {
requireComponents.core.historyStorage.deleteEverything()
reloadData()
}
is HistoryAction.Delete.One -> launch(Dispatchers.IO) {
requireComponents.core.historyStorage.deleteVisit(it.item.url, it.item.visitedAt)
reloadData()
}
is HistoryAction.Delete.Some -> launch(Dispatchers.IO) {
it.items.forEach { item ->
requireComponents.core.historyStorage.deleteVisit(item.url, item.visitedAt)
}
reloadData()
}
}
}
}
@ -128,4 +139,16 @@ class HistoryFragment : Fragment(), CoroutineScope, BackHandler {
}
override fun onBackPressed(): Boolean = (historyComponent.uiView as HistoryUIView).onBackPressed()
private suspend fun reloadData() {
val items = requireComponents.core.historyStorage.getDetailedVisits(0)
.asReversed()
.mapIndexed { id, item -> HistoryItem(id, item.url, item.visitTime) }
coroutineScope {
launch(Dispatchers.Main) {
getManagedEmitter<HistoryChange>().onNext(HistoryChange.Change(items))
}
}
}
}

View File

@ -6,11 +6,12 @@ package org.mozilla.fenix.library.history
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.widget.NestedScrollView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.functions.Consumer
import kotlinx.android.synthetic.main.component_history.view.*
import mozilla.components.support.base.feature.BackHandler
import org.mozilla.fenix.R
import org.mozilla.fenix.mvi.UIView
@ -26,22 +27,48 @@ class HistoryUIView(
var mode: HistoryState.Mode = HistoryState.Mode.Normal
private set
override val view: RecyclerView = LayoutInflater.from(container.context)
override val view: NestedScrollView = LayoutInflater.from(container.context)
.inflate(R.layout.component_history, container, true)
.findViewById(R.id.history_list)
.findViewById(R.id.history_wrapper)
init {
view.apply {
view.history_list.apply {
adapter = HistoryAdapter(actionEmitter)
layoutManager = LinearLayoutManager(container.context)
}
view.delete_history_button.setOnClickListener {
val mode = mode
val action = when (mode) {
is HistoryState.Mode.Normal -> HistoryAction.Delete.All
is HistoryState.Mode.Editing -> HistoryAction.Delete.Some(mode.selectedItems)
}
actionEmitter.onNext(action)
}
}
override fun updateView() = Consumer<HistoryState> {
mode = it.mode
(view.adapter as HistoryAdapter).updateData(it.items, it.mode)
updateDeleteButton()
(view.history_list.adapter as HistoryAdapter).updateData(it.items, it.mode)
}
private fun updateDeleteButton() {
val mode = mode
val text = if (mode is HistoryState.Mode.Editing && mode.selectedItems.isNotEmpty()) {
view.delete_history_button_text.context.resources.getString(
R.string.history_delete_some,
mode.selectedItems.size
)
} else {
view.delete_history_button_text.context.resources.getString(R.string.history_delete_all)
}
view.delete_history_button.contentDescription = text
view.delete_history_button_text.text = text
}
override fun onBackPressed(): Boolean {
if (mode is HistoryState.Mode.Editing) {
actionEmitter.onNext(HistoryAction.BackPressed)

View File

@ -3,8 +3,42 @@
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/history_list"
<androidx.core.widget.NestedScrollView
android:id="@+id/history_wrapper"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/delete_history_button"
android:foreground="?android:attr/selectableItemBackground"
android:background="@drawable/button_background"
android:layout_margin="16dp"
android:padding="6dp"
android:clickable="true"
android:focusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/delete_history_button_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/history_delete_all"
android:textColor="@color/photonRed60"
android:drawablePadding="8dp"
android:textSize="16sp"
android:gravity="center"
android:clickable="false"
android:focusable="false"
android:layout_gravity="center" />
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/history_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/delete_history_button"
android:foreground="?android:attr/selectableItemBackground"
android:background="@drawable/button_background"
android:layout_margin="16dp"
android:padding="6dp"
android:clickable="true"
android:focusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/delete_history_button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/history_delete_all"
android:textColor="@color/photonRed60"
android:drawablePadding="8dp"
android:textSize="16sp"
android:gravity="center"
android:clickable="false"
android:focusable="false"
android:layout_gravity="center" />
</FrameLayout>