Yuito-app-android/app/src/main/java/com/keylesspalace/tusky/network/FilterModel.kt

66 lines
2.2 KiB
Kotlin

package com.keylesspalace.tusky.network
import android.text.TextUtils
import com.keylesspalace.tusky.entity.Filter
import com.keylesspalace.tusky.entity.Status
import java.util.regex.Pattern
import javax.inject.Inject
/**
* One-stop for status filtering logic using Mastodon's filters.
*
* 1. You init with [initWithFilters], this compiles regex pattern.
* 2. You call [shouldFilterStatus] to figure out what to display when you load statuses.
*/
class FilterModel @Inject constructor() {
private var pattern: Pattern? = null
fun initWithFilters(filters: List<Filter>) {
this.pattern = makeFilter(filters)
}
fun shouldFilterStatus(status: Status): Boolean {
// Patterns are expensive and thread-safe, matchers are neither.
val matcher = pattern?.matcher("") ?: return false
if (status.poll != null) {
val pollMatches = status.poll.options.any { matcher.reset(it.title).find() }
if (pollMatches) return true
}
val spoilerText = status.actionableStatus.spoilerText
val attachmentsDescriptions = status.attachments
.mapNotNull { it.description }
return (
matcher.reset(status.actionableStatus.content).find() ||
(spoilerText.isNotEmpty() && matcher.reset(spoilerText).find()) ||
(
attachmentsDescriptions.isNotEmpty() &&
matcher.reset(attachmentsDescriptions.joinToString("\n")).find()
)
)
}
private fun filterToRegexToken(filter: Filter): String? {
val phrase = filter.phrase
val quotedPhrase = Pattern.quote(phrase)
return if (filter.wholeWord && ALPHANUMERIC.matcher(phrase).matches()) {
String.format("(^|\\W)%s($|\\W)", quotedPhrase)
} else {
quotedPhrase
}
}
private fun makeFilter(filters: List<Filter>): Pattern? {
if (filters.isEmpty()) return null
val tokens = filters.map { filterToRegexToken(it) }
return Pattern.compile(TextUtils.join("|", tokens), Pattern.CASE_INSENSITIVE)
}
companion object {
private val ALPHANUMERIC = Pattern.compile("^\\w+$")
}
}