mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-16 11:42:22 +08:00
feat: modify subsItem updateUrl (#727)
This commit is contained in:
parent
7ef4c51392
commit
1b5e60c67c
|
@ -0,0 +1,127 @@
|
|||
package li.songe.gkd.ui.component
|
||||
|
||||
import android.webkit.URLUtil
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import li.songe.gkd.MainActivity
|
||||
import li.songe.gkd.MainViewModel
|
||||
import li.songe.gkd.util.isSafeUrl
|
||||
import li.songe.gkd.util.launchAsFn
|
||||
import li.songe.gkd.util.subsItemsFlow
|
||||
import li.songe.gkd.util.throttle
|
||||
import li.songe.gkd.util.toast
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
|
||||
class InputSubsLinkOption {
|
||||
private val showFlow = MutableStateFlow(false)
|
||||
private val valueFlow = MutableStateFlow("")
|
||||
private val initValueFlow = MutableStateFlow("")
|
||||
private var continuation: Continuation<String?>? = null
|
||||
|
||||
private fun resume(value: String?) {
|
||||
showFlow.value = false
|
||||
valueFlow.value = ""
|
||||
initValueFlow.value = ""
|
||||
continuation?.resume(value)
|
||||
continuation = null
|
||||
}
|
||||
|
||||
private suspend fun submit(mainVm: MainViewModel) {
|
||||
val value = valueFlow.value
|
||||
if (!URLUtil.isNetworkUrl(value)) {
|
||||
toast("非法链接")
|
||||
return
|
||||
}
|
||||
val initValue = initValueFlow.value
|
||||
if (initValue.isNotEmpty() && initValue == value) {
|
||||
toast("未修改")
|
||||
resume(null)
|
||||
return
|
||||
}
|
||||
if (subsItemsFlow.value.any { it.updateUrl == value }) {
|
||||
toast("已有相同链接订阅")
|
||||
return
|
||||
}
|
||||
if (!isSafeUrl(value)) {
|
||||
mainVm.dialogFlow.waitResult(
|
||||
title = "未知来源",
|
||||
text = "你正在添加一个未验证的远程订阅\n\n这可能含有恶意的规则\n\n是否仍然确认添加?"
|
||||
)
|
||||
}
|
||||
resume(value)
|
||||
}
|
||||
|
||||
private fun cancel() = resume(null)
|
||||
|
||||
suspend fun getResult(initValue: String = ""): String? {
|
||||
initValueFlow.value = initValue
|
||||
valueFlow.value = initValue
|
||||
showFlow.value = true
|
||||
return suspendCoroutine {
|
||||
continuation = it
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContentDialog() {
|
||||
val show by showFlow.collectAsState()
|
||||
if (show) {
|
||||
val context = LocalContext.current as MainActivity
|
||||
val value by valueFlow.collectAsState()
|
||||
val initValue by initValueFlow.collectAsState()
|
||||
AlertDialog(
|
||||
title = {
|
||||
Text(text = if (initValue.isNotEmpty()) "修改订阅" else "添加订阅")
|
||||
},
|
||||
text = {
|
||||
OutlinedTextField(
|
||||
value = value,
|
||||
onValueChange = {
|
||||
valueFlow.value = it.trim()
|
||||
},
|
||||
maxLines = 8,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = {
|
||||
Text(text = "请输入订阅链接")
|
||||
},
|
||||
isError = value.isNotEmpty() && !URLUtil.isNetworkUrl(value),
|
||||
)
|
||||
},
|
||||
onDismissRequest = {
|
||||
if (valueFlow.value.isEmpty()) {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
enabled = value.isNotEmpty(),
|
||||
onClick = throttle(fn = context.mainVm.viewModelScope.launchAsFn {
|
||||
submit(context.mainVm)
|
||||
}),
|
||||
) {
|
||||
Text(text = "确定")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = ::cancel) {
|
||||
Text(text = "取消")
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -237,7 +237,8 @@ private fun SubsMenuItem(
|
|||
},
|
||||
onClick = throttle {
|
||||
onExpandedChange(false)
|
||||
navController.toDestinationsNavigator().navigate(SubsPageDestination(subItem.id))
|
||||
navController.toDestinationsNavigator()
|
||||
.navigate(SubsPageDestination(subItem.id))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -248,7 +249,8 @@ private fun SubsMenuItem(
|
|||
},
|
||||
onClick = throttle {
|
||||
onExpandedChange(false)
|
||||
navController.toDestinationsNavigator().navigate(CategoryPageDestination(subItem.id))
|
||||
navController.toDestinationsNavigator()
|
||||
.navigate(CategoryPageDestination(subItem.id))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -259,11 +261,23 @@ private fun SubsMenuItem(
|
|||
},
|
||||
onClick = throttle {
|
||||
onExpandedChange(false)
|
||||
navController.toDestinationsNavigator().navigate(GlobalRulePageDestination(subItem.id))
|
||||
navController.toDestinationsNavigator()
|
||||
.navigate(GlobalRulePageDestination(subItem.id))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
subscription?.supportUri?.let { supportUri ->
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(text = "问题反馈")
|
||||
},
|
||||
onClick = {
|
||||
onExpandedChange(false)
|
||||
context.openUri(supportUri)
|
||||
}
|
||||
)
|
||||
}
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(text = "导出数据")
|
||||
|
@ -286,15 +300,17 @@ private fun SubsMenuItem(
|
|||
toast("复制成功")
|
||||
}
|
||||
)
|
||||
}
|
||||
subscription?.supportUri?.let { supportUri ->
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(text = "问题反馈")
|
||||
Text(text = "修改链接")
|
||||
},
|
||||
onClick = {
|
||||
onExpandedChange(false)
|
||||
context.openUri(supportUri)
|
||||
vm.viewModelScope.launchTry {
|
||||
val newUrl = vm.inputSubsLinkOption.getResult(initValue = it)
|
||||
newUrl ?: return@launchTry
|
||||
vm.addOrModifySubs(newUrl, subItem)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package li.songe.gkd.ui.home
|
||||
|
||||
import android.webkit.URLUtil
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
|
@ -17,6 +16,7 @@ import li.songe.gkd.appScope
|
|||
import li.songe.gkd.data.RawSubscription
|
||||
import li.songe.gkd.data.SubsItem
|
||||
import li.songe.gkd.db.DbSet
|
||||
import li.songe.gkd.ui.component.InputSubsLinkOption
|
||||
import li.songe.gkd.util.SortTypeOption
|
||||
import li.songe.gkd.util.appInfoCacheFlow
|
||||
import li.songe.gkd.util.clickCountFlow
|
||||
|
@ -64,17 +64,12 @@ class HomeVm : ViewModel() {
|
|||
}.stateIn(appScope, SharingStarted.Eagerly, "")
|
||||
}
|
||||
|
||||
fun addSubsFromUrl(url: String) = viewModelScope.launchTry(Dispatchers.IO) {
|
||||
fun addOrModifySubs(
|
||||
url: String,
|
||||
oldItem: SubsItem? = null,
|
||||
) = viewModelScope.launchTry(Dispatchers.IO) {
|
||||
if (subsRefreshingFlow.value) return@launchTry
|
||||
if (!URLUtil.isNetworkUrl(url)) {
|
||||
toast("非法链接")
|
||||
return@launchTry
|
||||
}
|
||||
val subItems = subsItemsFlow.value
|
||||
if (subItems.any { it.updateUrl == url }) {
|
||||
toast("订阅链接已存在")
|
||||
return@launchTry
|
||||
}
|
||||
subsRefreshingFlow.value = true
|
||||
try {
|
||||
val text = try {
|
||||
|
@ -93,22 +88,34 @@ class HomeVm : ViewModel() {
|
|||
toast("解析订阅文件失败")
|
||||
return@launchTry
|
||||
}
|
||||
if (subItems.any { it.id == newSubsRaw.id }) {
|
||||
toast("订阅已存在")
|
||||
return@launchTry
|
||||
if (oldItem == null) {
|
||||
if (subItems.any { it.id == newSubsRaw.id }) {
|
||||
toast("订阅已存在")
|
||||
return@launchTry
|
||||
}
|
||||
} else {
|
||||
if (oldItem.id != newSubsRaw.id) {
|
||||
toast("订阅id不对应")
|
||||
return@launchTry
|
||||
}
|
||||
}
|
||||
if (newSubsRaw.id < 0) {
|
||||
toast("订阅id不可为${newSubsRaw.id}\n负数id为内部使用")
|
||||
return@launchTry
|
||||
}
|
||||
val newItem = SubsItem(
|
||||
val newItem = oldItem?.copy(updateUrl = url) ?: SubsItem(
|
||||
id = newSubsRaw.id,
|
||||
updateUrl = url,
|
||||
order = if (subItems.isEmpty()) 1 else (subItems.maxBy { it.order }.order + 1)
|
||||
)
|
||||
updateSubscription(newSubsRaw)
|
||||
DbSet.subsItemDao.insert(newItem)
|
||||
toast("成功添加订阅")
|
||||
if (oldItem == null) {
|
||||
DbSet.subsItemDao.insert(newItem)
|
||||
toast("成功添加订阅")
|
||||
} else {
|
||||
DbSet.subsItemDao.update(newItem)
|
||||
toast("成功修改订阅")
|
||||
}
|
||||
} finally {
|
||||
subsRefreshingFlow.value = false
|
||||
}
|
||||
|
@ -167,4 +174,6 @@ class HomeVm : ViewModel() {
|
|||
}.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
val showShareDataIdsFlow = MutableStateFlow<Set<Long>?>(null)
|
||||
|
||||
val inputSubsLinkOption = InputSubsLinkOption()
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package li.songe.gkd.ui.home
|
||||
|
||||
import android.content.Intent
|
||||
import android.webkit.URLUtil
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
|
@ -32,7 +31,6 @@ import androidx.compose.material3.DropdownMenuItem
|
|||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
|
@ -77,7 +75,6 @@ import li.songe.gkd.util.SafeR
|
|||
import li.songe.gkd.util.UpdateTimeOption
|
||||
import li.songe.gkd.util.checkSubsUpdate
|
||||
import li.songe.gkd.util.findOption
|
||||
import li.songe.gkd.util.isSafeUrl
|
||||
import li.songe.gkd.util.launchAsFn
|
||||
import li.songe.gkd.util.launchTry
|
||||
import li.songe.gkd.util.map
|
||||
|
@ -111,9 +108,6 @@ fun useSubsManagePage(): ScaffoldExt {
|
|||
orderSubItems = subItems
|
||||
}
|
||||
|
||||
var showAddLinkDialog by remember { mutableStateOf(false) }
|
||||
var link by remember { mutableStateOf("") }
|
||||
|
||||
val refreshing by subsRefreshingFlow.collectAsState()
|
||||
val pullRefreshState = rememberPullRefreshState(refreshing, { checkSubsUpdate(true) })
|
||||
var isSelectedMode by remember { mutableStateOf(false) }
|
||||
|
@ -135,59 +129,6 @@ fun useSubsManagePage(): ScaffoldExt {
|
|||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(showAddLinkDialog) {
|
||||
if (!showAddLinkDialog) {
|
||||
link = ""
|
||||
}
|
||||
}
|
||||
if (showAddLinkDialog) {
|
||||
AlertDialog(title = { Text(text = "添加订阅") }, text = {
|
||||
OutlinedTextField(
|
||||
value = link,
|
||||
onValueChange = { link = it.trim() },
|
||||
maxLines = 8,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = {
|
||||
Text(text = "请输入订阅链接")
|
||||
},
|
||||
isError = link.isNotEmpty() && !URLUtil.isNetworkUrl(link),
|
||||
)
|
||||
}, onDismissRequest = {
|
||||
if (link.isEmpty()) {
|
||||
showAddLinkDialog = false
|
||||
}
|
||||
}, dismissButton = {
|
||||
TextButton(onClick = {
|
||||
showAddLinkDialog = false
|
||||
}) {
|
||||
Text(text = "取消")
|
||||
}
|
||||
}, confirmButton = {
|
||||
TextButton(enabled = link.isNotBlank(), onClick = {
|
||||
if (!URLUtil.isNetworkUrl(link)) {
|
||||
toast("非法链接")
|
||||
return@TextButton
|
||||
}
|
||||
if (subItems.any { s -> s.updateUrl == link }) {
|
||||
toast("链接已存在")
|
||||
return@TextButton
|
||||
}
|
||||
vm.viewModelScope.launchTry {
|
||||
if (!isSafeUrl(link)) {
|
||||
context.mainVm.dialogFlow.waitResult(
|
||||
title = "未知来源",
|
||||
text = "你正在添加一个未验证的远程订阅\n\n这可能含有恶意的规则\n\n是否仍然确认添加?"
|
||||
)
|
||||
}
|
||||
showAddLinkDialog = false
|
||||
vm.addSubsFromUrl(url = link)
|
||||
}
|
||||
}) {
|
||||
Text(text = "确认")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var showSettingsDlg by remember { mutableStateOf(false) }
|
||||
if (showSettingsDlg) {
|
||||
AlertDialog(
|
||||
|
@ -212,6 +153,7 @@ fun useSubsManagePage(): ScaffoldExt {
|
|||
}
|
||||
|
||||
ShareDataDialog(vm)
|
||||
vm.inputSubsLinkOption.ContentDialog()
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
return ScaffoldExt(
|
||||
|
@ -389,7 +331,10 @@ fun useSubsManagePage(): ScaffoldExt {
|
|||
toast("正在刷新订阅,请稍后操作")
|
||||
return@FloatingActionButton
|
||||
}
|
||||
showAddLinkDialog = true
|
||||
vm.viewModelScope.launchTry {
|
||||
val url = vm.inputSubsLinkOption.getResult() ?: return@launchTry
|
||||
vm.addOrModifySubs(url)
|
||||
}
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Add,
|
||||
|
|
Loading…
Reference in New Issue
Block a user