perf: upload dialog, move log to AdvancedPage
Some checks are pending
Build-Apk / build (push) Waiting to run

This commit is contained in:
lisonge 2024-08-07 16:06:23 +08:00
parent eebcf242de
commit ab8afcefae
7 changed files with 249 additions and 341 deletions

View File

@ -15,12 +15,15 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Upload
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalTextStyle
@ -46,6 +49,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.core.content.ContextCompat
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewModelScope
@ -82,12 +86,16 @@ import li.songe.gkd.util.LocalLauncher
import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.ProfileTransitions
import li.songe.gkd.util.appInfoCacheFlow
import li.songe.gkd.util.buildLogFile
import li.songe.gkd.util.json
import li.songe.gkd.util.launchAsFn
import li.songe.gkd.util.launchTry
import li.songe.gkd.util.openApp
import li.songe.gkd.util.openUri
import li.songe.gkd.util.saveFileToDownloads
import li.songe.gkd.util.shareFile
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.throttle
import li.songe.gkd.util.toast
import rikka.shizuku.Shizuku
@ -103,10 +111,62 @@ fun AdvancedPage() {
val snapshotCount by vm.snapshotCountFlow.collectAsState()
ShizukuErrorDialog(vm.shizukuErrorFlow)
vm.uploadOptions.ShowDialog()
var showPortDlg by remember {
mutableStateOf(false)
}
var showShareLogDlg by remember {
mutableStateOf(false)
}
if (showShareLogDlg) {
Dialog(onDismissRequest = { showShareLogDlg = false }) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
val modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
Text(
text = "分享到其他应用", modifier = Modifier
.clickable(onClick = throttle {
showShareLogDlg = false
vm.viewModelScope.launchTry(Dispatchers.IO) {
val logZipFile = buildLogFile()
context.shareFile(logZipFile, "分享日志文件")
}
})
.then(modifier)
)
Text(
text = "保存到下载", modifier = Modifier
.clickable(onClick = throttle {
showShareLogDlg = false
vm.viewModelScope.launchTry(Dispatchers.IO) {
val logZipFile = buildLogFile()
context.saveFileToDownloads(logZipFile)
}
})
.then(modifier)
)
Text(
text = "生成链接(需科学上网)",
modifier = Modifier
.clickable(onClick = throttle {
showShareLogDlg = false
vm.viewModelScope.launchTry(Dispatchers.IO) {
val logZipFile = buildLogFile()
vm.uploadOptions.startTask(logZipFile)
}
})
.then(modifier)
)
}
}
}
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(
@ -324,7 +384,7 @@ fun AdvancedPage() {
}
TextSwitch(
name = "隐藏快照状态栏",
name = "隐藏状态栏",
desc = "当保存快照时,隐藏截图里的顶部状态栏高度区域",
checked = store.hideSnapshotStatusBar
) {
@ -334,7 +394,7 @@ fun AdvancedPage() {
}
TextSwitch(
name = "保存快照提示",
name = "保存提示",
desc = "保存快照时是否提示\"正在保存快照\"",
checked = store.showSaveSnapshotToast
) {
@ -343,6 +403,37 @@ fun AdvancedPage() {
)
}
Text(
text = "日志",
modifier = Modifier.titleItemPadding(),
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.primary,
)
TextSwitch(name = "保存日志",
desc = "保存7天日志,帮助定位BUG",
checked = store.log2FileSwitch,
onCheckedChange = {
storeFlow.value = store.copy(
log2FileSwitch = it
)
if (!it) {
context.mainVm.viewModelScope.launchTry(Dispatchers.IO) {
val logFiles = LogUtils.getLogFiles()
if (logFiles.isNotEmpty()) {
logFiles.forEach { f ->
f.delete()
}
toast("已删除全部日志")
}
}
}
})
SettingItem(title = "导出日志", imageVector = Icons.Default.Upload, onClick = {
showShareLogDlg = true
})
Text(
text = "其它",
modifier = Modifier.titleItemPadding(),

View File

@ -1,6 +1,5 @@
package li.songe.gkd.ui
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
@ -8,12 +7,15 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
import li.songe.gkd.db.DbSet
import li.songe.gkd.ui.component.UploadOptions
import javax.inject.Inject
@HiltViewModel
class AdvancedVm @Inject constructor(stateHandle: SavedStateHandle) : ViewModel() {
class AdvancedVm @Inject constructor() : ViewModel() {
val snapshotCountFlow =
DbSet.snapshotDao.count().stateIn(viewModelScope, SharingStarted.Eagerly, 0)
val shizukuErrorFlow = MutableStateFlow(false)
val uploadOptions = UploadOptions(viewModelScope)
}

View File

@ -15,17 +15,14 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
@ -49,7 +46,6 @@ import com.blankj.utilcode.util.UriUtils
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.ramcosta.composedestinations.navigation.navigate
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import li.songe.gkd.MainActivity
@ -63,7 +59,6 @@ import li.songe.gkd.ui.component.waitResult
import li.songe.gkd.ui.destinations.ImagePreviewPageDestination
import li.songe.gkd.ui.style.EmptyHeight
import li.songe.gkd.util.IMPORT_BASE_URL
import li.songe.gkd.util.LoadStatus
import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.LocalPickContentLauncher
import li.songe.gkd.util.ProfileTransitions
@ -86,7 +81,8 @@ fun SnapshotPage() {
val vm = hiltViewModel<SnapshotVm>()
val snapshots by vm.snapshotsState.collectAsState()
val uploadStatus by vm.uploadStatusFlow.collectAsState()
vm.uploadOptions.ShowDialog()
var selectedSnapshot by remember {
mutableStateOf<Snapshot?>(null)
@ -253,9 +249,9 @@ fun SnapshotPage() {
} else {
Text(
text = "生成链接(需科学上网)", modifier = Modifier
.clickable(onClick = {
.clickable(onClick = vm.viewModelScope.launchAsFn(Dispatchers.IO) {
selectedSnapshot = null
vm.uploadZip(snapshotVal)
vm.uploadOptions.startTask(SnapshotExt.getSnapshotZipFile(snapshotVal.id))
})
.then(modifier)
)
@ -323,69 +319,6 @@ fun SnapshotPage() {
}
}
}
when (val uploadStatusVal = uploadStatus) {
is LoadStatus.Failure -> {
AlertDialog(
title = { Text(text = "上传失败") },
text = {
Text(text = uploadStatusVal.exception.let {
it.message ?: it.toString()
})
},
onDismissRequest = { vm.uploadStatusFlow.value = null },
confirmButton = {
TextButton(onClick = {
vm.uploadStatusFlow.value = null
}) {
Text(text = "关闭")
}
},
)
}
is LoadStatus.Loading -> {
AlertDialog(
title = { Text(text = "上传文件中") },
text = {
LinearProgressIndicator(
progress = { uploadStatusVal.progress },
)
},
onDismissRequest = { },
confirmButton = {
TextButton(onClick = {
vm.uploadJob?.cancel(CancellationException("终止上传"))
vm.uploadJob = null
}) {
Text(text = "终止上传")
}
},
)
}
is LoadStatus.Success -> {
AlertDialog(title = { Text(text = "上传完成") }, text = {
Text(text = IMPORT_BASE_URL + uploadStatusVal.result.id)
}, onDismissRequest = {}, dismissButton = {
TextButton(onClick = {
vm.uploadStatusFlow.value = null
}) {
Text(text = "关闭")
}
}, confirmButton = {
TextButton(onClick = {
ClipboardUtils.copyText(IMPORT_BASE_URL + uploadStatusVal.result.id)
toast("复制成功")
vm.uploadStatusFlow.value = null
}) {
Text(text = "复制")
}
})
}
else -> {}
}
}

View File

@ -3,27 +3,10 @@ package li.songe.gkd.ui
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import io.ktor.client.call.body
import io.ktor.client.plugins.onUpload
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.client.statement.bodyAsText
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
import li.songe.gkd.data.GithubPoliciesAsset
import li.songe.gkd.data.RpcError
import li.songe.gkd.data.Snapshot
import li.songe.gkd.db.DbSet
import li.songe.gkd.debug.SnapshotExt.getSnapshotZipFile
import li.songe.gkd.util.FILE_UPLOAD_URL
import li.songe.gkd.util.LoadStatus
import li.songe.gkd.util.client
import li.songe.gkd.util.launchTry
import li.songe.gkd.ui.component.UploadOptions
import javax.inject.Inject
@ -32,40 +15,6 @@ class SnapshotVm @Inject constructor() : ViewModel() {
val snapshotsState = DbSet.snapshotDao.query()
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
val uploadStatusFlow = MutableStateFlow<LoadStatus<GithubPoliciesAsset>?>(null)
var uploadJob: Job? = null
fun uploadZip(snapshot: Snapshot) {
uploadJob = viewModelScope.launchTry(Dispatchers.IO) {
val zipFile = getSnapshotZipFile(snapshot.id)
uploadStatusFlow.value = LoadStatus.Loading()
try {
val response =
client.submitFormWithBinaryData(url = FILE_UPLOAD_URL, formData = formData {
append("\"file\"", zipFile.readBytes(), Headers.build {
append(HttpHeaders.ContentType, "application/x-zip-compressed")
append(HttpHeaders.ContentDisposition, "filename=\"file.zip\"")
})
}) {
onUpload { bytesSentTotal, contentLength ->
if (uploadStatusFlow.value is LoadStatus.Loading) {
uploadStatusFlow.value =
LoadStatus.Loading(bytesSentTotal / contentLength.toFloat())
}
}
}
if (response.headers["X_RPC_OK"] == "true") {
val policiesAsset = response.body<GithubPoliciesAsset>()
uploadStatusFlow.value = LoadStatus.Success(policiesAsset)
DbSet.snapshotDao.update(snapshot.copy(githubAssetId = policiesAsset.id))
} else if (response.headers["X_RPC_OK"] == "false") {
uploadStatusFlow.value = LoadStatus.Failure(response.body<RpcError>())
} else {
uploadStatusFlow.value = LoadStatus.Failure(Exception(response.bodyAsText()))
}
} catch (e: Exception) {
uploadStatusFlow.value = LoadStatus.Failure(e)
}
}
}
val uploadOptions = UploadOptions(viewModelScope)
}

View File

@ -0,0 +1,146 @@
package li.songe.gkd.ui.component
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import com.blankj.utilcode.util.ClipboardUtils
import io.ktor.client.call.body
import io.ktor.client.plugins.onUpload
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.client.statement.bodyAsText
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import li.songe.gkd.data.GithubPoliciesAsset
import li.songe.gkd.data.RpcError
import li.songe.gkd.util.FILE_UPLOAD_URL
import li.songe.gkd.util.LoadStatus
import li.songe.gkd.util.client
import li.songe.gkd.util.launchTry
import li.songe.gkd.util.toast
import java.io.File
class UploadOptions(
private val scope: CoroutineScope
) {
private val statusFlow = MutableStateFlow<LoadStatus<GithubPoliciesAsset>?>(null)
private var job: Job? = null
private fun buildTask(file: File) = scope.launchTry(Dispatchers.IO) {
statusFlow.value = LoadStatus.Loading()
try {
val response =
client.submitFormWithBinaryData(url = FILE_UPLOAD_URL, formData = formData {
append("\"file\"", file.readBytes(), Headers.build {
append(HttpHeaders.ContentType, "application/x-zip-compressed")
append(HttpHeaders.ContentDisposition, "filename=\"file.zip\"")
})
}) {
onUpload { bytesSentTotal, contentLength ->
if (statusFlow.value is LoadStatus.Loading) {
statusFlow.value =
LoadStatus.Loading(bytesSentTotal / contentLength.toFloat())
}
}
}
if (response.headers["X_RPC_OK"] == "true") {
val policiesAsset = response.body<GithubPoliciesAsset>()
statusFlow.value = LoadStatus.Success(policiesAsset)
} else if (response.headers["X_RPC_OK"] == "false") {
statusFlow.value = LoadStatus.Failure(response.body<RpcError>())
} else {
statusFlow.value = LoadStatus.Failure(Exception(response.bodyAsText()))
}
} catch (e: Exception) {
statusFlow.value = LoadStatus.Failure(e)
} finally {
job = null
}
}
fun startTask(file: File) {
if (job == null && statusFlow.value is LoadStatus.Loading) {
return
}
job = buildTask(file)
}
private fun stopTask() {
if (statusFlow.value is LoadStatus.Loading && job != null) {
job?.cancel(CancellationException("您取消了上传"))
job = null
}
}
@Composable
fun ShowDialog() {
when (val status = statusFlow.collectAsState().value) {
null -> {}
is LoadStatus.Loading -> {
AlertDialog(
title = { Text(text = "上传文件中") },
text = {
LinearProgressIndicator(
progress = { status.progress },
)
},
onDismissRequest = { },
confirmButton = {
TextButton(onClick = {
stopTask()
}) {
Text(text = "终止上传")
}
},
)
}
is LoadStatus.Success -> {
AlertDialog(title = { Text(text = "上传完成") }, text = {
Text(text = status.result.shortHref)
}, onDismissRequest = {}, dismissButton = {
TextButton(onClick = {
statusFlow.value = null
}) {
Text(text = "关闭")
}
}, confirmButton = {
TextButton(onClick = {
ClipboardUtils.copyText(status.result.shortHref)
toast("复制成功")
statusFlow.value = null
}) {
Text(text = "复制并关闭")
}
})
}
is LoadStatus.Failure -> {
AlertDialog(
title = { Text(text = "上传失败") },
text = {
Text(text = status.exception.let {
it.message ?: it.toString()
})
},
onDismissRequest = { statusFlow.value = null },
confirmButton = {
TextButton(onClick = {
statusFlow.value = null
}) {
Text(text = "关闭")
}
},
)
}
}
}
}

View File

@ -5,16 +5,9 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.blankj.utilcode.util.LogUtils
import dagger.hilt.android.lifecycle.HiltViewModel
import io.ktor.client.call.body
import io.ktor.client.plugins.onUpload
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
@ -22,13 +15,9 @@ import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import li.songe.gkd.appScope
import li.songe.gkd.data.GithubPoliciesAsset
import li.songe.gkd.data.RawSubscription
import li.songe.gkd.data.RpcError
import li.songe.gkd.data.SubsItem
import li.songe.gkd.db.DbSet
import li.songe.gkd.util.FILE_UPLOAD_URL
import li.songe.gkd.util.LoadStatus
import li.songe.gkd.util.SortTypeOption
import li.songe.gkd.util.appInfoCacheFlow
import li.songe.gkd.util.clickCountFlow
@ -44,48 +33,12 @@ import li.songe.gkd.util.subsItemsFlow
import li.songe.gkd.util.subsRefreshingFlow
import li.songe.gkd.util.toast
import li.songe.gkd.util.updateSubscription
import java.io.File
import javax.inject.Inject
@HiltViewModel
class HomeVm @Inject constructor() : ViewModel() {
val tabFlow = MutableStateFlow(controlNav)
val uploadStatusFlow = MutableStateFlow<LoadStatus<GithubPoliciesAsset>?>(null)
var uploadJob: Job? = null
fun uploadZip(zipFile: File) {
uploadJob = viewModelScope.launchTry(Dispatchers.IO) {
uploadStatusFlow.value = LoadStatus.Loading()
try {
val response =
client.submitFormWithBinaryData(url = FILE_UPLOAD_URL, formData = formData {
append("\"file\"", zipFile.readBytes(), Headers.build {
append(HttpHeaders.ContentType, "application/x-zip-compressed")
append(HttpHeaders.ContentDisposition, "filename=\"file.zip\"")
})
}) {
onUpload { bytesSentTotal, contentLength ->
if (uploadStatusFlow.value is LoadStatus.Loading) {
uploadStatusFlow.value =
LoadStatus.Loading(bytesSentTotal / contentLength.toFloat())
}
}
}
if (response.headers["X_RPC_OK"] == "true") {
val policiesAsset = response.body<GithubPoliciesAsset>()
uploadStatusFlow.value = LoadStatus.Success(policiesAsset)
} else if (response.headers["X_RPC_OK"] == "false") {
uploadStatusFlow.value = LoadStatus.Failure(response.body<RpcError>())
} else {
uploadStatusFlow.value = LoadStatus.Failure(Exception(response.bodyAsText()))
}
} catch (e: Exception) {
uploadStatusFlow.value = LoadStatus.Failure(e)
}
}
}
private val latestRecordFlow =
DbSet.clickLogDao.queryLatest().stateIn(viewModelScope, SharingStarted.Eagerly, null)
val latestRecordDescFlow = combine(

View File

@ -9,17 +9,13 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
import androidx.compose.material.icons.filled.Upload
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
@ -35,20 +31,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewModelScope
import com.blankj.utilcode.util.ClipboardUtils
import com.blankj.utilcode.util.LogUtils
import com.ramcosta.composedestinations.navigation.navigate
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.update
import li.songe.gkd.BuildConfig.ENABLED_UPDATE
import li.songe.gkd.MainActivity
import li.songe.gkd.ui.component.RotatingLoadingIcon
import li.songe.gkd.ui.component.SettingItem
import li.songe.gkd.ui.component.TextMenu
@ -61,17 +49,12 @@ import li.songe.gkd.ui.style.itemPadding
import li.songe.gkd.ui.style.titleItemPadding
import li.songe.gkd.ui.theme.supportDynamicColor
import li.songe.gkd.util.DarkThemeOption
import li.songe.gkd.util.LoadStatus
import li.songe.gkd.util.LocalMainViewModel
import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.UpdateTimeOption
import li.songe.gkd.util.buildLogFile
import li.songe.gkd.util.checkUpdate
import li.songe.gkd.util.findOption
import li.songe.gkd.util.launchAsFn
import li.songe.gkd.util.launchTry
import li.songe.gkd.util.saveFileToDownloads
import li.songe.gkd.util.shareFile
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.throttle
import li.songe.gkd.util.toast
@ -82,12 +65,10 @@ val settingsNav = BottomNavItem(
@Composable
fun useSettingsPage(): ScaffoldExt {
val context = LocalContext.current as MainActivity
val mainVm = LocalMainViewModel.current
val navController = LocalNavController.current
val store by storeFlow.collectAsState()
val vm = hiltViewModel<HomeVm>()
val uploadStatus by vm.uploadStatusFlow.collectAsState()
var showToastInputDlg by remember {
mutableStateOf(false)
@ -96,10 +77,6 @@ fun useSettingsPage(): ScaffoldExt {
mutableStateOf(false)
}
var showShareLogDlg by remember {
mutableStateOf(false)
}
val checkUpdating by mainVm.updateStatus.checkUpdatingFlow.collectAsState()
if (showToastInputDlg) {
@ -210,118 +187,6 @@ fun useSettingsPage(): ScaffoldExt {
})
}
if (showShareLogDlg) {
Dialog(onDismissRequest = { showShareLogDlg = false }) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
val modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
Text(
text = "分享到其他应用", modifier = Modifier
.clickable(onClick = throttle {
showShareLogDlg = false
vm.viewModelScope.launchTry(Dispatchers.IO) {
val logZipFile = buildLogFile()
context.shareFile(logZipFile, "分享日志文件")
}
})
.then(modifier)
)
Text(
text = "保存到下载", modifier = Modifier
.clickable(onClick = throttle {
showShareLogDlg = false
vm.viewModelScope.launchTry(Dispatchers.IO) {
val logZipFile = buildLogFile()
context.saveFileToDownloads(logZipFile)
}
})
.then(modifier)
)
Text(
text = "生成链接(需科学上网)",
modifier = Modifier
.clickable(onClick = throttle {
showShareLogDlg = false
vm.viewModelScope.launchTry(Dispatchers.IO) {
val logZipFile = buildLogFile()
vm.uploadZip(logZipFile)
}
})
.then(modifier)
)
}
}
}
when (val uploadStatusVal = uploadStatus) {
is LoadStatus.Failure -> {
AlertDialog(
title = { Text(text = "上传失败") },
text = {
Text(text = uploadStatusVal.exception.let {
it.message ?: it.toString()
})
},
onDismissRequest = { vm.uploadStatusFlow.value = null },
confirmButton = {
TextButton(onClick = {
vm.uploadStatusFlow.value = null
}) {
Text(text = "关闭")
}
},
)
}
is LoadStatus.Loading -> {
AlertDialog(
title = { Text(text = "上传文件中") },
text = {
LinearProgressIndicator(
progress = { uploadStatusVal.progress },
)
},
onDismissRequest = { },
confirmButton = {
TextButton(onClick = {
vm.uploadJob?.cancel(CancellationException("终止上传"))
vm.uploadJob = null
}) {
Text(text = "终止上传")
}
},
)
}
is LoadStatus.Success -> {
AlertDialog(title = { Text(text = "上传完成") }, text = {
Text(text = uploadStatusVal.result.shortHref)
}, onDismissRequest = {}, dismissButton = {
TextButton(onClick = {
vm.uploadStatusFlow.value = null
}) {
Text(text = "关闭")
}
}, confirmButton = {
TextButton(onClick = {
ClipboardUtils.copyText(uploadStatusVal.result.shortHref)
toast("复制成功")
vm.uploadStatusFlow.value = null
}) {
Text(text = "复制")
}
})
}
else -> {}
}
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val scrollState = rememberScrollState()
return ScaffoldExt(
@ -470,37 +335,6 @@ fun useSettingsPage(): ScaffoldExt {
}
}
Text(
text = "日志",
modifier = Modifier.titleItemPadding(),
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.primary,
)
TextSwitch(name = "保存日志",
desc = "保存7天日志,帮助定位BUG",
checked = store.log2FileSwitch,
onCheckedChange = {
storeFlow.value = store.copy(
log2FileSwitch = it
)
if (!it) {
mainVm.viewModelScope.launchTry(Dispatchers.IO) {
val logFiles = LogUtils.getLogFiles()
if (logFiles.isNotEmpty()) {
logFiles.forEach { f ->
f.delete()
}
toast("已删除全部日志")
}
}
}
})
SettingItem(title = "导出日志", imageVector = Icons.Default.Upload, onClick = {
showShareLogDlg = true
})
Text(
text = "其它",
modifier = Modifier.titleItemPadding(),