Respect filter expiration date when applying filters (#2661)

* Respect filter expiration date when applying filters. #2578

* Fix typing for filter `expires_in` api points
This commit is contained in:
Levi Bard 2022-08-15 11:01:04 +02:00 committed by GitHub
parent be4645ec38
commit b21def5041
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 45 additions and 7 deletions

View File

@ -56,7 +56,7 @@ class FiltersActivity : BaseActivity() {
} }
private fun updateFilter(filter: Filter, itemIndex: Int) { private fun updateFilter(filter: Filter, itemIndex: Int) {
api.updateFilter(filter.id, filter.phrase, filter.context, filter.irreversible, filter.wholeWord, filter.expiresAt) api.updateFilter(filter.id, filter.phrase, filter.context, filter.irreversible, filter.wholeWord, null)
.enqueue(object : Callback<Filter> { .enqueue(object : Callback<Filter> {
override fun onFailure(call: Call<Filter>, t: Throwable) { override fun onFailure(call: Call<Filter>, t: Throwable) {
Toast.makeText(this@FiltersActivity, "Error updating filter '${filter.phrase}'", Toast.LENGTH_SHORT).show() Toast.makeText(this@FiltersActivity, "Error updating filter '${filter.phrase}'", Toast.LENGTH_SHORT).show()
@ -102,7 +102,7 @@ class FiltersActivity : BaseActivity() {
} }
private fun createFilter(phrase: String, wholeWord: Boolean) { private fun createFilter(phrase: String, wholeWord: Boolean) {
api.createFilter(phrase, listOf(context), false, wholeWord, "").enqueue(object : Callback<Filter> { api.createFilter(phrase, listOf(context), false, wholeWord, null).enqueue(object : Callback<Filter> {
override fun onResponse(call: Call<Filter>, response: Response<Filter>) { override fun onResponse(call: Call<Filter>, response: Response<Filter>) {
val filterResponse = response.body() val filterResponse = response.body()
if (response.isSuccessful && filterResponse != null) { if (response.isSuccessful && filterResponse != null) {

View File

@ -16,12 +16,13 @@
package com.keylesspalace.tusky.entity package com.keylesspalace.tusky.entity
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import java.util.Date
data class Filter( data class Filter(
val id: String, val id: String,
val phrase: String, val phrase: String,
val context: List<String>, val context: List<String>,
@SerializedName("expires_at") val expiresAt: String?, @SerializedName("expires_at") val expiresAt: Date?,
val irreversible: Boolean, val irreversible: Boolean,
@SerializedName("whole_word") val wholeWord: Boolean @SerializedName("whole_word") val wholeWord: Boolean
) { ) {

View File

@ -3,6 +3,7 @@ package com.keylesspalace.tusky.network
import android.text.TextUtils import android.text.TextUtils
import com.keylesspalace.tusky.entity.Filter import com.keylesspalace.tusky.entity.Filter
import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.entity.Status
import java.util.Date
import java.util.regex.Pattern import java.util.regex.Pattern
import javax.inject.Inject import javax.inject.Inject
@ -54,7 +55,9 @@ class FilterModel @Inject constructor() {
private fun makeFilter(filters: List<Filter>): Pattern? { private fun makeFilter(filters: List<Filter>): Pattern? {
if (filters.isEmpty()) return null if (filters.isEmpty()) return null
val tokens = filters.map { filterToRegexToken(it) } val tokens = filters
.filter { it.expiresAt?.before(Date()) != true }
.map { filterToRegexToken(it) }
return Pattern.compile(TextUtils.join("|", tokens), Pattern.CASE_INSENSITIVE) return Pattern.compile(TextUtils.join("|", tokens), Pattern.CASE_INSENSITIVE)
} }

View File

@ -536,7 +536,7 @@ interface MastodonApi {
@Field("context[]") context: List<String>, @Field("context[]") context: List<String>,
@Field("irreversible") irreversible: Boolean?, @Field("irreversible") irreversible: Boolean?,
@Field("whole_word") wholeWord: Boolean?, @Field("whole_word") wholeWord: Boolean?,
@Field("expires_in") expiresIn: String? @Field("expires_in") expiresIn: Int?
): Call<Filter> ): Call<Filter>
@FormUrlEncoded @FormUrlEncoded
@ -547,7 +547,7 @@ interface MastodonApi {
@Field("context[]") context: List<String>, @Field("context[]") context: List<String>,
@Field("irreversible") irreversible: Boolean?, @Field("irreversible") irreversible: Boolean?,
@Field("whole_word") wholeWord: Boolean?, @Field("whole_word") wholeWord: Boolean?,
@Field("expires_in") expiresIn: String? @Field("expires_in") expiresIn: Int?
): Call<Filter> ): Call<Filter>
@DELETE("api/v1/filters/{id}") @DELETE("api/v1/filters/{id}")

View File

@ -14,6 +14,7 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.kotlin.mock import org.mockito.kotlin.mock
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
import java.time.Instant
import java.util.ArrayList import java.util.ArrayList
import java.util.Date import java.util.Date
@ -50,7 +51,23 @@ class FilterTest {
expiresAt = null, expiresAt = null,
irreversible = false, irreversible = false,
wholeWord = true wholeWord = true
) ),
Filter(
id = "123",
phrase = "expired",
context = listOf(Filter.HOME),
expiresAt = Date.from(Instant.now().minusSeconds(10)),
irreversible = false,
wholeWord = true
),
Filter(
id = "123",
phrase = "unexpired",
context = listOf(Filter.HOME),
expiresAt = Date.from(Instant.now().plusSeconds(3600)),
irreversible = false,
wholeWord = true
),
) )
filterModel.initWithFilters(filters) filterModel.initWithFilters(filters)
@ -148,6 +165,23 @@ class FilterTest {
) )
} }
@Test
fun shouldNotFilter_whenFilterIsExpired() {
assertFalse(
filterModel.shouldFilterStatus(
mockStatus(content = "content matching expired filter should not be filtered")
)
)
}
@Test
fun shouldFilter_whenFilterIsUnexpired() {
assertTrue(
filterModel.shouldFilterStatus(
mockStatus(content = "content matching unexpired filter should be filtered")
)
)
}
private fun mockStatus( private fun mockStatus(
content: String = "", content: String = "",
spoilerText: String = "", spoilerText: String = "",