pref: 屏蔽管理
This commit is contained in:
parent
ce39a5fdf2
commit
972d6b2ee6
|
|
@ -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.disableComponent
|
||||
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.EmoticonManager
|
||||
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.UpdateTrack
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.litepal.LitePal
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
|
||||
@HiltAndroidApp
|
||||
|
|
@ -141,15 +140,12 @@ class App : Application(), IApp, SketchFactory {
|
|||
registerActivityLifecycleCallbacks(ClipBoardLinkDetector)
|
||||
registerActivityLifecycleCallbacks(OAIDGetter)
|
||||
PluginManager.init(this)
|
||||
CoroutineScope(Dispatchers.IO).apply {
|
||||
launch {
|
||||
thread {
|
||||
BlockManager.init()
|
||||
EmoticonManager.init(this@App)
|
||||
}
|
||||
launch {
|
||||
ClientUtils.init(this@App)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//解决魅族 Flyme 系统夜间模式强制反色
|
||||
@Keep
|
||||
|
|
|
|||
|
|
@ -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.ui.common.PbContentRender
|
||||
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.utils.getPhotoViewData
|
||||
import com.huanchengfly.tieba.post.utils.EmoticonManager
|
||||
import com.huanchengfly.tieba.post.utils.EmoticonUtil.emoticonString
|
||||
import com.huanchengfly.tieba.post.utils.ImageUtil
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
|
||||
private val defaultUserAgent: String =
|
||||
|
|
@ -101,6 +104,17 @@ private val PbContent.picUrl: String
|
|||
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)
|
||||
val List<PbContent>.renders: List<PbContentRender>
|
||||
get() {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
package com.huanchengfly.tieba.post.models.database
|
||||
|
||||
import com.huanchengfly.tieba.post.fromJson
|
||||
import org.litepal.crud.LitePalSupport
|
||||
|
||||
data class Block @JvmOverloads constructor(
|
||||
var category: Int = 0,
|
||||
var type: Int = 0,
|
||||
var keywords: String? = null,
|
||||
var username: String? = null,
|
||||
var uid: String? = null,
|
||||
val category: Int = 0,
|
||||
val type: Int = 0,
|
||||
val keywords: String? = null,
|
||||
val username: String? = null,
|
||||
val uid: String? = null,
|
||||
) : LitePalSupport() {
|
||||
val id: Long = 0L
|
||||
companion object {
|
||||
|
|
@ -16,5 +17,9 @@ data class Block @JvmOverloads constructor(
|
|||
|
||||
const val TYPE_KEYWORD = 0
|
||||
const val TYPE_USER = 1
|
||||
|
||||
fun Block.getKeywords(): List<String> {
|
||||
return keywords?.fromJson() ?: emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ import androidx.compose.material.icons.outlined.AccountCircle
|
|||
import androidx.compose.material.icons.outlined.Block
|
||||
import androidx.compose.material.icons.outlined.CheckCircle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -77,10 +78,15 @@ fun BlockSettingsPage(
|
|||
var addBlockCategory by remember { mutableStateOf(Block.CATEGORY_BLACK_LIST) }
|
||||
val dialogState = rememberDialogState()
|
||||
PromptDialog(
|
||||
dialogState = dialogState,
|
||||
onConfirm = {
|
||||
viewModel.send(BlockSettingsUiIntent.Add(category = addBlockCategory, keywords = it.split(" ")))
|
||||
viewModel.send(
|
||||
BlockSettingsUiIntent.Add(
|
||||
category = addBlockCategory,
|
||||
keywords = it.split(" ")
|
||||
)
|
||||
)
|
||||
},
|
||||
dialogState = dialogState,
|
||||
title = {
|
||||
Text(
|
||||
text = if (addBlockCategory == Block.CATEGORY_WHITE_LIST) stringResource(id = R.string.title_add_white)
|
||||
|
|
@ -199,7 +205,11 @@ fun BlockSettingsPage(
|
|||
contentPadding = paddingValues,
|
||||
verticalAlignment = Alignment.Top
|
||||
) { position ->
|
||||
val items = if (position == 0) blackList else whiteList
|
||||
val items by remember {
|
||||
derivedStateOf {
|
||||
if (position == 0) blackList else whiteList
|
||||
}
|
||||
}
|
||||
StateScreen(
|
||||
isEmpty = items.isEmpty(),
|
||||
isError = false,
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorMessage
|
|||
import com.huanchengfly.tieba.post.arch.*
|
||||
import com.huanchengfly.tieba.post.models.database.Block
|
||||
import com.huanchengfly.tieba.post.toJson
|
||||
import com.huanchengfly.tieba.post.utils.BlockManager
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.litepal.LitePal
|
||||
import org.litepal.extension.delete
|
||||
|
||||
class BlockSettingsViewModel :
|
||||
BaseViewModel<BlockSettingsUiIntent, BlockSettingsPartialChange, BlockSettingsUiState, BlockSettingsUiEvent>() {
|
||||
|
|
@ -36,22 +35,27 @@ class BlockSettingsViewModel :
|
|||
)
|
||||
|
||||
private fun produceLoadPartialChange(): Flow<BlockSettingsPartialChange.Load> =
|
||||
flow<BlockSettingsPartialChange.Load> {
|
||||
val blocks = LitePal.findAll(Block::class.java)
|
||||
val blackList = blocks.filter { it.category == Block.CATEGORY_BLACK_LIST }
|
||||
val whiteList = blocks.filter { it.category == Block.CATEGORY_WHITE_LIST }
|
||||
emit(BlockSettingsPartialChange.Load.Success(blackList, whiteList))
|
||||
}.onStart { BlockSettingsPartialChange.Load.Start }
|
||||
flowOf<BlockSettingsPartialChange.Load>(
|
||||
BlockSettingsPartialChange.Load.Success(
|
||||
BlockManager.blackList,
|
||||
BlockManager.whiteList
|
||||
)
|
||||
)
|
||||
.onStart { BlockSettingsPartialChange.Load.Start }
|
||||
.catch { emit(BlockSettingsPartialChange.Load.Failure(it)) }
|
||||
|
||||
private fun BlockSettingsUiIntent.Add.producePartialChange(): 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)) }
|
||||
|
||||
private fun BlockSettingsUiIntent.Delete.producePartialChange(): Flow<BlockSettingsPartialChange.Delete> =
|
||||
flow<BlockSettingsPartialChange.Delete> {
|
||||
LitePal.delete<Block>(id = id)
|
||||
BlockManager.removeBlock(id)
|
||||
emit(BlockSettingsPartialChange.Delete.Success(id))
|
||||
}.catch { emit(BlockSettingsPartialChange.Delete.Failure(it)) }
|
||||
}
|
||||
|
|
@ -91,11 +95,17 @@ sealed interface BlockSettingsPartialChange : PartialChange<BlockSettingsUiState
|
|||
when (this) {
|
||||
is Success -> {
|
||||
val newBlackList =
|
||||
if (item.category == Block.CATEGORY_BLACK_LIST) oldState.blackList + item
|
||||
else oldState.blackList
|
||||
if (item.category == Block.CATEGORY_BLACK_LIST) {
|
||||
oldState.blackList + item
|
||||
} else {
|
||||
oldState.blackList
|
||||
}
|
||||
val newWhiteList =
|
||||
if (item.category == Block.CATEGORY_WHITE_LIST) oldState.whiteList + item
|
||||
else oldState.whiteList
|
||||
if (item.category == Block.CATEGORY_WHITE_LIST) {
|
||||
oldState.whiteList + item
|
||||
} else {
|
||||
oldState.whiteList
|
||||
}
|
||||
oldState.copy(blackList = newBlackList, whiteList = newWhiteList)
|
||||
}
|
||||
is Failure -> oldState
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
Loading…
Reference in New Issue