) {
- v1 = true
- this.pattern = makeFilter(filters)
+ init {
+ pattern = v1filters?.let { list ->
+ makeFilter(list.filter { it.context.contains(filterKind.kind) })
+ }
}
- fun shouldFilterStatus(status: Status): Filter.Action {
- if (v1) {
+ /** @return the [Filter.Action] that should be applied to this status */
+ fun filterActionFor(status: Status): Filter.Action {
+ pattern?.let { pat ->
// Patterns are expensive and thread-safe, matchers are neither.
- val matcher = pattern?.matcher("") ?: return Filter.Action.NONE
+ val matcher = pat.matcher("") ?: return Filter.Action.NONE
if (status.poll?.options?.any { matcher.reset(it.title).find() } == true) {
return Filter.Action.HIDE
@@ -48,7 +48,7 @@ class FilterModel @Inject constructor() {
}
val matchingKind = status.filtered?.filter { result ->
- result.filter.kinds.contains(kind)
+ result.filter.kinds.contains(filterKind)
}
return if (matchingKind.isNullOrEmpty()) {
diff --git a/app/src/test/java/app/pachli/FilterV1Test.kt b/app/src/test/java/app/pachli/FilterV1Test.kt
index 0cc72f001..47317fcd3 100644
--- a/app/src/test/java/app/pachli/FilterV1Test.kt
+++ b/app/src/test/java/app/pachli/FilterV1Test.kt
@@ -41,7 +41,6 @@ class FilterV1Test {
@Before
fun setup() {
- filterModel = FilterModel()
val filters = listOf(
FilterV1(
id = "123",
@@ -101,14 +100,14 @@ class FilterV1Test {
),
)
- filterModel.initWithFilters(filters)
+ filterModel = FilterModel(Filter.Kind.HOME, filters)
}
@Test
fun shouldNotFilter() {
assertEquals(
Filter.Action.NONE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(content = "should not be filtered"),
),
)
@@ -118,7 +117,7 @@ class FilterV1Test {
fun shouldFilter_whenContentMatchesBadWord() {
assertEquals(
Filter.Action.HIDE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(content = "one two badWord three"),
),
)
@@ -128,7 +127,7 @@ class FilterV1Test {
fun shouldFilter_whenContentMatchesBadWordPart() {
assertEquals(
Filter.Action.HIDE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(content = "one two badWordPart three"),
),
)
@@ -138,7 +137,7 @@ class FilterV1Test {
fun shouldFilter_whenContentMatchesBadWholeWord() {
assertEquals(
Filter.Action.HIDE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(content = "one two badWholeWord three"),
),
)
@@ -148,7 +147,7 @@ class FilterV1Test {
fun shouldNotFilter_whenContentDoesNotMatchWholeWord() {
assertEquals(
Filter.Action.NONE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(content = "one two badWholeWordTest three"),
),
)
@@ -158,7 +157,7 @@ class FilterV1Test {
fun shouldFilter_whenSpoilerTextDoesMatch() {
assertEquals(
Filter.Action.HIDE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(
content = "should not be filtered",
spoilerText = "badWord should be filtered",
@@ -171,7 +170,7 @@ class FilterV1Test {
fun shouldFilter_whenPollTextDoesMatch() {
assertEquals(
Filter.Action.HIDE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(
content = "should not be filtered",
spoilerText = "should not be filtered",
@@ -185,7 +184,7 @@ class FilterV1Test {
fun shouldFilter_whenMediaDescriptionDoesMatch() {
assertEquals(
Filter.Action.HIDE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(
content = "should not be filtered",
spoilerText = "should not be filtered",
@@ -199,7 +198,7 @@ class FilterV1Test {
fun shouldFilterPartialWord_whenWholeWordFilterContainsNonAlphanumericCharacters() {
assertEquals(
Filter.Action.HIDE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(content = "one two someone@twitter.com three"),
),
)
@@ -209,7 +208,7 @@ class FilterV1Test {
fun shouldFilterHashtags() {
assertEquals(
Filter.Action.HIDE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(content = "#hashtag one two three"),
),
)
@@ -219,7 +218,7 @@ class FilterV1Test {
fun shouldFilterHashtags_whenContentIsMarkedUp() {
assertEquals(
Filter.Action.HIDE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(content = "#hashtagone two three
"),
),
)
@@ -229,7 +228,7 @@ class FilterV1Test {
fun shouldNotFilterHtmlAttributes() {
assertEquals(
Filter.Action.NONE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(content = "https://foo.bar/ one two three
"),
),
)
@@ -239,7 +238,7 @@ class FilterV1Test {
fun shouldNotFilter_whenFilterIsExpired() {
assertEquals(
Filter.Action.NONE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(content = "content matching expired filter should not be filtered"),
),
)
@@ -249,7 +248,7 @@ class FilterV1Test {
fun shouldFilter_whenFilterIsUnexpired() {
assertEquals(
Filter.Action.HIDE,
- filterModel.shouldFilterStatus(
+ filterModel.filterActionFor(
mockStatus(content = "content matching unexpired filter should be filtered"),
),
)
diff --git a/app/src/test/java/app/pachli/components/notifications/NotificationsViewModelTestBase.kt b/app/src/test/java/app/pachli/components/notifications/NotificationsViewModelTestBase.kt
index cf67757de..cee902a18 100644
--- a/app/src/test/java/app/pachli/components/notifications/NotificationsViewModelTestBase.kt
+++ b/app/src/test/java/app/pachli/components/notifications/NotificationsViewModelTestBase.kt
@@ -19,12 +19,12 @@ package app.pachli.components.notifications
import androidx.test.ext.junit.runners.AndroidJUnit4
import app.pachli.appstore.EventHub
+import app.pachli.components.timeline.FilterKind
import app.pachli.components.timeline.FiltersRepository
import app.pachli.components.timeline.MainCoroutineRule
import app.pachli.db.AccountEntity
import app.pachli.db.AccountManager
import app.pachli.fakes.InMemorySharedPreferences
-import app.pachli.network.FilterModel
import app.pachli.settings.AccountPreferenceDataStore
import app.pachli.usecase.TimelineCases
import app.pachli.util.SharedPreferencesRepository
@@ -51,7 +51,6 @@ abstract class NotificationsViewModelTestBase {
protected lateinit var viewModel: NotificationsViewModel
private lateinit var statusDisplayOptionsRepository: StatusDisplayOptionsRepository
private lateinit var filtersRepository: FiltersRepository
- private lateinit var filterModel: FilterModel
private val eventHub = EventHub()
@@ -96,8 +95,9 @@ abstract class NotificationsViewModelTestBase {
)
timelineCases = mock()
- filtersRepository = mock()
- filterModel = mock()
+ filtersRepository = mock {
+ onBlocking { getFilters() } doReturn FilterKind.V2(emptyList())
+ }
sharedPreferencesRepository = SharedPreferencesRepository(
InMemorySharedPreferences(),
@@ -117,7 +117,6 @@ abstract class NotificationsViewModelTestBase {
timelineCases,
eventHub,
filtersRepository,
- filterModel,
statusDisplayOptionsRepository,
sharedPreferencesRepository,
)
diff --git a/app/src/test/java/app/pachli/components/timeline/CachedTimelineViewModelTestBase.kt b/app/src/test/java/app/pachli/components/timeline/CachedTimelineViewModelTestBase.kt
index b4a62a23e..965809ea0 100644
--- a/app/src/test/java/app/pachli/components/timeline/CachedTimelineViewModelTestBase.kt
+++ b/app/src/test/java/app/pachli/components/timeline/CachedTimelineViewModelTestBase.kt
@@ -17,6 +17,7 @@
package app.pachli.components.timeline
+import androidx.lifecycle.SavedStateHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import app.pachli.PachliApplication
import app.pachli.appstore.EventHub
@@ -24,7 +25,6 @@ import app.pachli.components.timeline.viewmodel.CachedTimelineViewModel
import app.pachli.components.timeline.viewmodel.TimelineViewModel
import app.pachli.db.AccountManager
import app.pachli.entity.Account
-import app.pachli.network.FilterModel
import app.pachli.network.MastodonApi
import app.pachli.settings.AccountPreferenceDataStore
import app.pachli.usecase.TimelineCases
@@ -76,19 +76,19 @@ abstract class CachedTimelineViewModelTestBase {
@Inject
lateinit var sharedPreferencesRepository: SharedPreferencesRepository
- private lateinit var cachedTimelineRepository: CachedTimelineRepository
+ @Inject
+ lateinit var filtersRepository: FiltersRepository
+
+ @Inject
+ lateinit var cachedTimelineRepository: CachedTimelineRepository
+
private lateinit var accountPreferenceDataStore: AccountPreferenceDataStore
protected lateinit var timelineCases: TimelineCases
private lateinit var statusDisplayOptionsRepository: StatusDisplayOptionsRepository
- private lateinit var filtersRepository: FiltersRepository
- private lateinit var filterModel: FilterModel
protected lateinit var viewModel: TimelineViewModel
private val eventHub = EventHub()
- /** Empty success response, for API calls that return one */
- protected var emptySuccess = Response.success("".toResponseBody())
-
/** Empty error response, for API calls that return one */
private var emptyError: Response = Response.error(404, "".toResponseBody())
@@ -102,6 +102,7 @@ abstract class CachedTimelineViewModelTestBase {
reset(mastodonApi)
mastodonApi.stub {
onBlocking { getCustomEmojis() } doReturn NetworkResult.failure(Exception())
+ onBlocking { getFilters() } doReturn NetworkResult.success(emptyList())
}
accountManager.addAccount(
@@ -123,16 +124,12 @@ abstract class CachedTimelineViewModelTestBase {
),
)
- cachedTimelineRepository = mock()
-
accountPreferenceDataStore = AccountPreferenceDataStore(
accountManager,
TestScope(),
)
timelineCases = mock()
- filtersRepository = mock()
- filterModel = mock()
statusDisplayOptionsRepository = StatusDisplayOptionsRepository(
sharedPreferencesRepository,
@@ -142,6 +139,7 @@ abstract class CachedTimelineViewModelTestBase {
)
viewModel = CachedTimelineViewModel(
+ SavedStateHandle(mapOf(TimelineViewModel.TIMELINE_KIND_TAG to TimelineKind.Home)),
cachedTimelineRepository,
timelineCases,
eventHub,
@@ -149,11 +147,7 @@ abstract class CachedTimelineViewModelTestBase {
accountManager,
statusDisplayOptionsRepository,
sharedPreferencesRepository,
- filterModel,
Gson(),
)
-
- // Initialisation with the Home timeline
- viewModel.init(TimelineKind.Home)
}
}
diff --git a/app/src/test/java/app/pachli/components/timeline/NetworkTimelineViewModelTestBase.kt b/app/src/test/java/app/pachli/components/timeline/NetworkTimelineViewModelTestBase.kt
index 7d0671577..713810ccf 100644
--- a/app/src/test/java/app/pachli/components/timeline/NetworkTimelineViewModelTestBase.kt
+++ b/app/src/test/java/app/pachli/components/timeline/NetworkTimelineViewModelTestBase.kt
@@ -17,17 +17,19 @@
package app.pachli.components.timeline
+import androidx.lifecycle.SavedStateHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import app.pachli.appstore.EventHub
import app.pachli.components.timeline.viewmodel.NetworkTimelineViewModel
import app.pachli.components.timeline.viewmodel.TimelineViewModel
import app.pachli.db.AccountManager
import app.pachli.entity.Account
-import app.pachli.network.FilterModel
+import app.pachli.network.MastodonApi
import app.pachli.settings.AccountPreferenceDataStore
import app.pachli.usecase.TimelineCases
import app.pachli.util.SharedPreferencesRepository
import app.pachli.util.StatusDisplayOptionsRepository
+import at.connyduck.calladapter.networkresult.NetworkResult
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import kotlinx.coroutines.test.TestScope
@@ -36,7 +38,10 @@ import okhttp3.ResponseBody.Companion.toResponseBody
import org.junit.Before
import org.junit.Rule
import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.stub
import org.robolectric.annotation.Config
import retrofit2.HttpException
import retrofit2.Response
@@ -57,22 +62,25 @@ abstract class NetworkTimelineViewModelTestBase {
@Inject
lateinit var accountManager: AccountManager
+ @Inject
+ lateinit var mastodonApi: MastodonApi
+
@Inject
lateinit var sharedPreferencesRepository: SharedPreferencesRepository
- private lateinit var networkTimelineRepository: NetworkTimelineRepository
+ @Inject
+ lateinit var filtersRepository: FiltersRepository
+
+ @Inject
+ lateinit var networkTimelineRepository: NetworkTimelineRepository
+
private lateinit var accountPreferenceDataStore: AccountPreferenceDataStore
protected lateinit var timelineCases: TimelineCases
- private lateinit var filtersRepository: FiltersRepository
private lateinit var statusDisplayOptionsRepository: StatusDisplayOptionsRepository
- private lateinit var filterModel: FilterModel
protected lateinit var viewModel: TimelineViewModel
private val eventHub = EventHub()
- /** Empty success response, for API calls that return one */
- protected var emptySuccess = Response.success("".toResponseBody())
-
/** Empty error response, for API calls that return one */
private var emptyError: Response = Response.error(404, "".toResponseBody())
@@ -83,6 +91,12 @@ abstract class NetworkTimelineViewModelTestBase {
fun setup() {
hilt.inject()
+ reset(mastodonApi)
+ mastodonApi.stub {
+ onBlocking { getCustomEmojis() } doReturn NetworkResult.failure(Exception())
+ onBlocking { getFilters() } doReturn NetworkResult.success(emptyList())
+ }
+
accountManager.addAccount(
accessToken = "token",
domain = "domain.example",
@@ -102,16 +116,12 @@ abstract class NetworkTimelineViewModelTestBase {
),
)
- networkTimelineRepository = mock()
-
accountPreferenceDataStore = AccountPreferenceDataStore(
accountManager,
TestScope(),
)
timelineCases = mock()
- filtersRepository = mock()
- filterModel = mock()
statusDisplayOptionsRepository = StatusDisplayOptionsRepository(
sharedPreferencesRepository,
@@ -121,6 +131,7 @@ abstract class NetworkTimelineViewModelTestBase {
)
viewModel = NetworkTimelineViewModel(
+ SavedStateHandle(mapOf(TimelineViewModel.TIMELINE_KIND_TAG to TimelineKind.Bookmarks)),
networkTimelineRepository,
timelineCases,
eventHub,
@@ -128,11 +139,6 @@ abstract class NetworkTimelineViewModelTestBase {
accountManager,
statusDisplayOptionsRepository,
sharedPreferencesRepository,
- filterModel,
)
-
- // Initialisation with any timeline kind, as long as it's not Home
- // (Home uses CachedTimelineViewModel)
- viewModel.init(TimelineKind.Bookmarks)
}
}
diff --git a/app/src/test/java/app/pachli/components/viewthread/ViewThreadViewModelTest.kt b/app/src/test/java/app/pachli/components/viewthread/ViewThreadViewModelTest.kt
index d42afb47e..86ca8a0e1 100644
--- a/app/src/test/java/app/pachli/components/viewthread/ViewThreadViewModelTest.kt
+++ b/app/src/test/java/app/pachli/components/viewthread/ViewThreadViewModelTest.kt
@@ -18,7 +18,6 @@ import app.pachli.db.AccountManager
import app.pachli.db.TimelineDao
import app.pachli.entity.Account
import app.pachli.entity.StatusContext
-import app.pachli.network.FilterModel
import app.pachli.network.MastodonApi
import app.pachli.settings.AccountPreferenceDataStore
import app.pachli.usecase.TimelineCases
@@ -130,8 +129,6 @@ class ViewThreadViewModelTest {
onBlocking { getFilters() } doReturn FilterKind.V2(emptyList())
}
- val filterModel = FilterModel()
-
val defaultAccount = AccountEntity(
id = 1,
domain = "mastodon.test",
@@ -178,7 +175,6 @@ class ViewThreadViewModelTest {
viewModel = ViewThreadViewModel(
mastodonApi,
- filterModel,
timelineCases,
eventHub,
accountManager,