pref: 屏蔽管理

This commit is contained in:
HuanCheng65 2023-07-13 18:17:53 +08:00
parent ce39a5fdf2
commit 972d6b2ee6
No known key found for this signature in database
GPG Key ID: 5EC9DD60A32C7360
6 changed files with 131 additions and 33 deletions

View File

@ -40,6 +40,7 @@ import com.huanchengfly.tieba.post.utils.AccountUtil
import com.huanchengfly.tieba.post.utils.AppIconUtil import com.huanchengfly.tieba.post.utils.AppIconUtil
import com.huanchengfly.tieba.post.utils.AppIconUtil.disableComponent import com.huanchengfly.tieba.post.utils.AppIconUtil.disableComponent
import com.huanchengfly.tieba.post.utils.AppIconUtil.enableComponent import com.huanchengfly.tieba.post.utils.AppIconUtil.enableComponent
import com.huanchengfly.tieba.post.utils.BlockManager
import com.huanchengfly.tieba.post.utils.ClientUtils import com.huanchengfly.tieba.post.utils.ClientUtils
import com.huanchengfly.tieba.post.utils.EmoticonManager import com.huanchengfly.tieba.post.utils.EmoticonManager
import com.huanchengfly.tieba.post.utils.Icons import com.huanchengfly.tieba.post.utils.Icons
@ -60,10 +61,8 @@ import com.microsoft.appcenter.distribute.ReleaseDetails
import com.microsoft.appcenter.distribute.UpdateAction import com.microsoft.appcenter.distribute.UpdateAction
import com.microsoft.appcenter.distribute.UpdateTrack import com.microsoft.appcenter.distribute.UpdateTrack
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.litepal.LitePal import org.litepal.LitePal
import kotlin.concurrent.thread
@HiltAndroidApp @HiltAndroidApp
@ -141,15 +140,12 @@ class App : Application(), IApp, SketchFactory {
registerActivityLifecycleCallbacks(ClipBoardLinkDetector) registerActivityLifecycleCallbacks(ClipBoardLinkDetector)
registerActivityLifecycleCallbacks(OAIDGetter) registerActivityLifecycleCallbacks(OAIDGetter)
PluginManager.init(this) PluginManager.init(this)
CoroutineScope(Dispatchers.IO).apply { thread {
launch { BlockManager.init()
EmoticonManager.init(this@App) EmoticonManager.init(this@App)
}
launch {
ClientUtils.init(this@App) ClientUtils.init(this@App)
} }
} }
}
//解决魅族 Flyme 系统夜间模式强制反色 //解决魅族 Flyme 系统夜间模式强制反色
@Keep @Keep

View File

@ -17,12 +17,15 @@ import com.huanchengfly.tieba.post.api.models.protos.ThreadInfo
import com.huanchengfly.tieba.post.arch.wrapImmutable import com.huanchengfly.tieba.post.arch.wrapImmutable
import com.huanchengfly.tieba.post.ui.common.PbContentRender import com.huanchengfly.tieba.post.ui.common.PbContentRender
import com.huanchengfly.tieba.post.ui.common.PicContentRender import com.huanchengfly.tieba.post.ui.common.PicContentRender
import com.huanchengfly.tieba.post.ui.common.TextContentRender import com.huanchengfly.tieba.post.ui.common.TextContentRender.Companion.appendText
import com.huanchengfly.tieba.post.ui.common.VideoContentRender
import com.huanchengfly.tieba.post.ui.common.theme.utils.ThemeUtils import com.huanchengfly.tieba.post.ui.common.theme.utils.ThemeUtils
import com.huanchengfly.tieba.post.ui.utils.getPhotoViewData import com.huanchengfly.tieba.post.ui.utils.getPhotoViewData
import com.huanchengfly.tieba.post.utils.EmoticonManager import com.huanchengfly.tieba.post.utils.EmoticonManager
import com.huanchengfly.tieba.post.utils.EmoticonUtil.emoticonString import com.huanchengfly.tieba.post.utils.EmoticonUtil.emoticonString
import com.huanchengfly.tieba.post.utils.ImageUtil import com.huanchengfly.tieba.post.utils.ImageUtil
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
private val defaultUserAgent: String = private val defaultUserAgent: String =
@ -101,6 +104,17 @@ private val PbContent.picUrl: String
src src
) )
val List<PbContent>.plainText: String
get() = joinToString(separator = "") {
when (it.type) {
0, 1, 4, 9, 27 -> it.text
2 -> "#(${it.c})"
3, 20 -> "[图片]"
5 -> "[视频]"
else -> ""
}
}
@OptIn(ExperimentalTextApi::class) @OptIn(ExperimentalTextApi::class)
val List<PbContent>.renders: List<PbContentRender> val List<PbContent>.renders: List<PbContentRender>
get() { get() {

View File

@ -1,13 +1,14 @@
package com.huanchengfly.tieba.post.models.database package com.huanchengfly.tieba.post.models.database
import com.huanchengfly.tieba.post.fromJson
import org.litepal.crud.LitePalSupport import org.litepal.crud.LitePalSupport
data class Block @JvmOverloads constructor( data class Block @JvmOverloads constructor(
var category: Int = 0, val category: Int = 0,
var type: Int = 0, val type: Int = 0,
var keywords: String? = null, val keywords: String? = null,
var username: String? = null, val username: String? = null,
var uid: String? = null, val uid: String? = null,
) : LitePalSupport() { ) : LitePalSupport() {
val id: Long = 0L val id: Long = 0L
companion object { companion object {
@ -16,5 +17,9 @@ data class Block @JvmOverloads constructor(
const val TYPE_KEYWORD = 0 const val TYPE_KEYWORD = 0
const val TYPE_USER = 1 const val TYPE_USER = 1
fun Block.getKeywords(): List<String> {
return keywords?.fromJson() ?: emptyList()
}
} }
} }

View File

@ -27,6 +27,7 @@ import androidx.compose.material.icons.outlined.AccountCircle
import androidx.compose.material.icons.outlined.Block import androidx.compose.material.icons.outlined.Block
import androidx.compose.material.icons.outlined.CheckCircle import androidx.compose.material.icons.outlined.CheckCircle
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -77,10 +78,15 @@ fun BlockSettingsPage(
var addBlockCategory by remember { mutableStateOf(Block.CATEGORY_BLACK_LIST) } var addBlockCategory by remember { mutableStateOf(Block.CATEGORY_BLACK_LIST) }
val dialogState = rememberDialogState() val dialogState = rememberDialogState()
PromptDialog( PromptDialog(
dialogState = dialogState,
onConfirm = { onConfirm = {
viewModel.send(BlockSettingsUiIntent.Add(category = addBlockCategory, keywords = it.split(" "))) viewModel.send(
BlockSettingsUiIntent.Add(
category = addBlockCategory,
keywords = it.split(" ")
)
)
}, },
dialogState = dialogState,
title = { title = {
Text( Text(
text = if (addBlockCategory == Block.CATEGORY_WHITE_LIST) stringResource(id = R.string.title_add_white) text = if (addBlockCategory == Block.CATEGORY_WHITE_LIST) stringResource(id = R.string.title_add_white)
@ -199,7 +205,11 @@ fun BlockSettingsPage(
contentPadding = paddingValues, contentPadding = paddingValues,
verticalAlignment = Alignment.Top verticalAlignment = Alignment.Top
) { position -> ) { position ->
val items = if (position == 0) blackList else whiteList val items by remember {
derivedStateOf {
if (position == 0) blackList else whiteList
}
}
StateScreen( StateScreen(
isEmpty = items.isEmpty(), isEmpty = items.isEmpty(),
isError = false, isError = false,

View File

@ -4,10 +4,9 @@ import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorMessage
import com.huanchengfly.tieba.post.arch.* import com.huanchengfly.tieba.post.arch.*
import com.huanchengfly.tieba.post.models.database.Block import com.huanchengfly.tieba.post.models.database.Block
import com.huanchengfly.tieba.post.toJson import com.huanchengfly.tieba.post.toJson
import com.huanchengfly.tieba.post.utils.BlockManager
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import org.litepal.LitePal
import org.litepal.extension.delete
class BlockSettingsViewModel : class BlockSettingsViewModel :
BaseViewModel<BlockSettingsUiIntent, BlockSettingsPartialChange, BlockSettingsUiState, BlockSettingsUiEvent>() { BaseViewModel<BlockSettingsUiIntent, BlockSettingsPartialChange, BlockSettingsUiState, BlockSettingsUiEvent>() {
@ -36,22 +35,27 @@ class BlockSettingsViewModel :
) )
private fun produceLoadPartialChange(): Flow<BlockSettingsPartialChange.Load> = private fun produceLoadPartialChange(): Flow<BlockSettingsPartialChange.Load> =
flow<BlockSettingsPartialChange.Load> { flowOf<BlockSettingsPartialChange.Load>(
val blocks = LitePal.findAll(Block::class.java) BlockSettingsPartialChange.Load.Success(
val blackList = blocks.filter { it.category == Block.CATEGORY_BLACK_LIST } BlockManager.blackList,
val whiteList = blocks.filter { it.category == Block.CATEGORY_WHITE_LIST } BlockManager.whiteList
emit(BlockSettingsPartialChange.Load.Success(blackList, whiteList)) )
}.onStart { BlockSettingsPartialChange.Load.Start } )
.onStart { BlockSettingsPartialChange.Load.Start }
.catch { emit(BlockSettingsPartialChange.Load.Failure(it)) } .catch { emit(BlockSettingsPartialChange.Load.Failure(it)) }
private fun BlockSettingsUiIntent.Add.producePartialChange(): Flow<BlockSettingsPartialChange.Add> = private fun BlockSettingsUiIntent.Add.producePartialChange(): Flow<BlockSettingsPartialChange.Add> =
flow<BlockSettingsPartialChange.Add> { flow<BlockSettingsPartialChange.Add> {
emit(BlockSettingsPartialChange.Add.Success(Block(category = category, type = Block.TYPE_KEYWORD, keywords = keywords.toJson()).apply { save() })) val block = Block(
category = category, type = Block.TYPE_KEYWORD, keywords = keywords.toJson()
)
BlockManager.addBlock(block)
emit(BlockSettingsPartialChange.Add.Success(block))
}.catch { emit(BlockSettingsPartialChange.Add.Failure(it)) } }.catch { emit(BlockSettingsPartialChange.Add.Failure(it)) }
private fun BlockSettingsUiIntent.Delete.producePartialChange(): Flow<BlockSettingsPartialChange.Delete> = private fun BlockSettingsUiIntent.Delete.producePartialChange(): Flow<BlockSettingsPartialChange.Delete> =
flow<BlockSettingsPartialChange.Delete> { flow<BlockSettingsPartialChange.Delete> {
LitePal.delete<Block>(id = id) BlockManager.removeBlock(id)
emit(BlockSettingsPartialChange.Delete.Success(id)) emit(BlockSettingsPartialChange.Delete.Success(id))
}.catch { emit(BlockSettingsPartialChange.Delete.Failure(it)) } }.catch { emit(BlockSettingsPartialChange.Delete.Failure(it)) }
} }
@ -91,11 +95,17 @@ sealed interface BlockSettingsPartialChange : PartialChange<BlockSettingsUiState
when (this) { when (this) {
is Success -> { is Success -> {
val newBlackList = val newBlackList =
if (item.category == Block.CATEGORY_BLACK_LIST) oldState.blackList + item if (item.category == Block.CATEGORY_BLACK_LIST) {
else oldState.blackList oldState.blackList + item
} else {
oldState.blackList
}
val newWhiteList = val newWhiteList =
if (item.category == Block.CATEGORY_WHITE_LIST) oldState.whiteList + item if (item.category == Block.CATEGORY_WHITE_LIST) {
else oldState.whiteList oldState.whiteList + item
} else {
oldState.whiteList
}
oldState.copy(blackList = newBlackList, whiteList = newWhiteList) oldState.copy(blackList = newBlackList, whiteList = newWhiteList)
} }
is Failure -> oldState is Failure -> oldState

View File

@ -0,0 +1,63 @@
package com.huanchengfly.tieba.post.utils
import com.huanchengfly.tieba.post.api.abstractText
import com.huanchengfly.tieba.post.api.models.protos.Post
import com.huanchengfly.tieba.post.api.models.protos.ThreadInfo
import com.huanchengfly.tieba.post.api.plainText
import com.huanchengfly.tieba.post.models.database.Block
import com.huanchengfly.tieba.post.models.database.Block.Companion.getKeywords
import org.litepal.LitePal
import org.litepal.extension.delete
import org.litepal.extension.findAllAsync
object BlockManager {
private val blockList: MutableList<Block> = mutableListOf()
val blackList: List<Block>
get() = blockList.filter { it.category == Block.CATEGORY_BLACK_LIST }
val whiteList: List<Block>
get() = blockList.filter { it.category == Block.CATEGORY_WHITE_LIST }
fun addBlock(block: Block) {
block.save()
blockList.add(block)
}
fun removeBlock(id: Long) {
LitePal.delete<Block>(id)
blockList.removeAll { it.id == id }
}
fun init() {
LitePal.findAllAsync<Block>().listen { blocks ->
blockList.addAll(blocks)
}
}
fun shouldBlock(content: String): Boolean {
return blackList.any { block ->
block.type == Block.TYPE_KEYWORD
&& block.getKeywords().all { content.contains(it) }
} && whiteList.none { block ->
block.type == Block.TYPE_KEYWORD
&& block.getKeywords().all { content.contains(it) }
}
}
fun shouldBlock(userId: Long = 0L, userName: String? = null): Boolean {
return blackList.any { block ->
block.type == Block.TYPE_USER
&& (block.uid == userId.toString() || block.username == userName)
} && whiteList.none { block ->
block.type == Block.TYPE_USER
&& (block.uid == userId.toString() || block.username == userName)
}
}
fun ThreadInfo.shouldBlock(): Boolean =
shouldBlock(abstractText) || shouldBlock(authorId, author?.name)
fun Post.shouldBlock(): Boolean =
shouldBlock(content.plainText) || shouldBlock(author_id, author?.name)
}