1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
/* 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/. */
package org.mozilla.fenix.home.blocklist
import mozilla.components.lib.state.Middleware
import mozilla.components.lib.state.MiddlewareContext
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.appstate.AppState
import org.mozilla.fenix.home.recenttabs.RecentTab
/**
* This [Middleware] reacts to item removals from the home screen, adding them to a blocklist.
* Additionally, it reacts to state changes in order to filter them by the blocklist.
*
* @param blocklistHandler An instance of [BlocklistHandler] for interacting with the blocklist
* stored in shared preferences.
*/
class BlocklistMiddleware(
private val blocklistHandler: BlocklistHandler,
) : Middleware<AppState, AppAction> {
/**
* Will filter "Change" actions using the blocklist and use "Remove" actions to update
* the blocklist.
*/
override fun invoke(
context: MiddlewareContext<AppState, AppAction>,
next: (AppAction) -> Unit,
action: AppAction,
) {
next(getUpdatedAction(context.state, action))
}
@Suppress("ComplexMethod")
private fun getUpdatedAction(
state: AppState,
action: AppAction,
) = with(blocklistHandler) {
when (action) {
is AppAction.Change -> {
action.copy(
bookmarks = action.bookmarks.filteredByBlocklist(),
recentTabs = action.recentTabs.filteredByBlocklist().filterContile(),
recentHistory = action.recentHistory.filteredByBlocklist().filterContile(),
recentSyncedTabState = action.recentSyncedTabState.filteredByBlocklist().filterContile(),
)
}
is AppAction.RecentTabsChange -> {
action.copy(
recentTabs = action.recentTabs.filteredByBlocklist().filterContile(),
)
}
is AppAction.BookmarksChange -> {
action.copy(
bookmarks = action.bookmarks.filteredByBlocklist(),
)
}
is AppAction.RecentHistoryChange -> {
action.copy(recentHistory = action.recentHistory.filteredByBlocklist().filterContile())
}
is AppAction.RecentSyncedTabStateChange -> {
action.copy(
state = action.state.filteredByBlocklist().filterContile(),
)
}
is AppAction.RemoveRecentTab -> {
if (action.recentTab is RecentTab.Tab) {
addUrlToBlocklist(action.recentTab.state.content.url)
state.toActionFilteringAllState(this)
} else {
action
}
}
is AppAction.RemoveBookmark -> {
action.bookmark.url?.let { url ->
addUrlToBlocklist(url)
state.toActionFilteringAllState(this)
} ?: action
}
is AppAction.RemoveRecentHistoryHighlight -> {
addUrlToBlocklist(action.highlightUrl)
state.toActionFilteringAllState(this)
}
is AppAction.RemoveRecentSyncedTab -> {
addUrlToBlocklist(action.syncedTab.url)
state.toActionFilteringAllState(this)
}
else -> action
}
}
// When an item is removed from any part of the state, it should also be removed from any other
// relevant parts that contain it.
// This is a candidate for refactoring once context receivers lands in Kotlin 1.6.20
// https://blog.jetbrains.com/kotlin/2022/02/kotlin-1-6-20-m1-released/#prototype-of-context-receivers-for-kotlin-jvm
private fun AppState.toActionFilteringAllState(blocklistHandler: BlocklistHandler) =
with(blocklistHandler) {
AppAction.Change(
recentTabs = recentTabs.filteredByBlocklist().filterContile(),
bookmarks = bookmarks.filteredByBlocklist(),
recentHistory = recentHistory.filteredByBlocklist().filterContile(),
topSites = topSites,
mode = mode,
collections = collections,
showCollectionPlaceholder = showCollectionPlaceholder,
recentSyncedTabState = recentSyncedTabState.filteredByBlocklist().filterContile(),
)
}
}
|