Cleanup dialog api
This commit is contained in:
1 parent
cfe7ee7f9c
commit
39b896e1b9
10 files changed
+83
-101
No files matched your search
@@ -300,7 +300,8 @@ suspend fun DialogState.doGalleryInfoAction(info: BaseGalleryInfo, context: Cont
|
||||
0 -> navToReader(info)
|
||||
1 -> withUIContext {
|
||||
if (downloaded) {
|
||||
if (confirmRemoveDownload(info)) DownloadManager.deleteDownload(info.gid)
|
||||
confirmRemoveDownload(info)
|
||||
DownloadManager.deleteDownload(info.gid)
|
||||
} else {
|
||||
CommonOperations.startDownload(this@with, info, false)
|
||||
}
|
||||
@@ -330,7 +331,7 @@ suspend fun DialogState.doGalleryInfoAction(info: BaseGalleryInfo, context: Cont
|
||||
|
||||
private const val MAX_FAVNOTE_CHAR = 200
|
||||
|
||||
suspend fun DialogState.confirmRemoveDownload(info: GalleryInfo): Boolean = show(
|
||||
suspend fun DialogState.confirmRemoveDownload(info: GalleryInfo) = awaitPermissionOrCancel(
|
||||
confirmText = android.R.string.ok,
|
||||
dismissText = android.R.string.cancel,
|
||||
title = R.string.download_remove_dialog_title,
|
||||
|
||||
@@ -88,7 +88,7 @@ fun CookieSignInScene(windowSizeClass: WindowSizeClass) {
|
||||
var signInJob by remember { mutableStateOf<Job?>(null) }
|
||||
|
||||
val dialogState = rememberDialogState()
|
||||
dialogState.Handler()
|
||||
dialogState.Intercept()
|
||||
|
||||
val noCookies = stringResource(R.string.from_clipboard_error)
|
||||
|
||||
@@ -129,7 +129,7 @@ fun CookieSignInScene(windowSizeClass: WindowSizeClass) {
|
||||
withUIContext { navController.navigate(if (canEx) SELECT_SITE_ROUTE_NAME else FINISH_ROUTE_NAME) }
|
||||
}.onFailure {
|
||||
EhCookieStore.signOut()
|
||||
dialogState.show(
|
||||
dialogState.awaitPermissionOrCancel(
|
||||
confirmText = R.string.get_it,
|
||||
title = R.string.sign_in_failed,
|
||||
text = {
|
||||
|
||||
@@ -89,7 +89,7 @@ fun SignInScreen(windowSizeClass: WindowSizeClass) {
|
||||
var signInJob by remember { mutableStateOf<Job?>(null) }
|
||||
|
||||
val dialogState = rememberDialogState()
|
||||
dialogState.Handler()
|
||||
dialogState.Intercept()
|
||||
|
||||
BackHandler {
|
||||
(context as Activity).moveTaskToBack(true)
|
||||
@@ -119,7 +119,7 @@ fun SignInScreen(windowSizeClass: WindowSizeClass) {
|
||||
}.onFailure {
|
||||
withUIContext {
|
||||
focusManager.clearFocus()
|
||||
dialogState.show(
|
||||
dialogState.awaitPermissionOrCancel(
|
||||
confirmText = R.string.get_it,
|
||||
title = R.string.sign_in_failed,
|
||||
text = {
|
||||
|
||||
@@ -407,7 +407,7 @@ class GalleryDetailScene : BaseScene() {
|
||||
updateDownloadState()
|
||||
}
|
||||
}
|
||||
dialogState.Handler()
|
||||
dialogState.Intercept()
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
Scaffold(
|
||||
topBar = {
|
||||
|
||||
@@ -313,7 +313,7 @@ class GalleryListScene : SearchBarScene() {
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
_binding = SceneGalleryListBinding.inflate(inflater, container!!)
|
||||
container.addView(ComposeView(inflater.context).apply { setMD3Content { dialogState.Handler() } })
|
||||
container.addView(ComposeView(inflater.context).apply { setMD3Content { dialogState.Intercept() } })
|
||||
checkForUpdates()
|
||||
requireActivity().onBackPressedDispatcher.addCallback(stateBackPressedCallback)
|
||||
mHideActionFabSlop = ViewConfiguration.get(requireContext()).scaledTouchSlop
|
||||
|
||||
@@ -76,7 +76,7 @@ class GalleryPreviewScreen : Fragment() {
|
||||
val columnCount = calculateSuitableSpanCount()
|
||||
val state = rememberLazyGridState()
|
||||
val dialogState = rememberDialogState()
|
||||
dialogState.Handler()
|
||||
dialogState.Intercept()
|
||||
val coroutineScope = rememberCoroutineScope { Dispatchers.IO }
|
||||
val pages = galleryDetail.pages
|
||||
val pgSize = galleryDetail.previewList.size
|
||||
|
||||
@@ -76,7 +76,7 @@ class HistoryScene : BaseScene() {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = ComposeView(inflater.context).apply {
|
||||
setMD3Content {
|
||||
val dialogState = rememberDialogState()
|
||||
dialogState.Handler()
|
||||
dialogState.Intercept()
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
val historyData = remember { Pager(PagingConfig(20, jumpThreshold = 40)) { EhDB.historyLazyList }.flow.cachedIn(lifecycleScope) }.collectAsLazyPagingItems()
|
||||
@@ -93,12 +93,12 @@ class HistoryScene : BaseScene() {
|
||||
actions = {
|
||||
IconButton(onClick = {
|
||||
coroutineScope.launchIO {
|
||||
val clear = dialogState.show(
|
||||
dialogState.awaitPermissionOrCancel(
|
||||
confirmText = R.string.clear_all,
|
||||
dismissText = android.R.string.cancel,
|
||||
text = { Text(text = stringResource(id = R.string.clear_all_history)) },
|
||||
)
|
||||
if (clear) EhDB.clearHistoryInfo()
|
||||
EhDB.clearHistoryInfo()
|
||||
}
|
||||
}) {
|
||||
Icon(imageVector = Icons.Default.ClearAll, contentDescription = null)
|
||||
|
||||
@@ -68,7 +68,7 @@ fun AboutScreen() {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val coroutineScope = rememberCoroutineScope { Dispatchers.IO }
|
||||
val dialogState = rememberDialogState()
|
||||
dialogState.Handler()
|
||||
dialogState.Intercept()
|
||||
fun launchSnackBar(content: String) = coroutineScope.launch { snackbarHostState.showSnackbar(content) }
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@@ -133,7 +133,7 @@ fun AboutScreen() {
|
||||
}
|
||||
|
||||
suspend fun DialogState.showNewVersion(context: Context, release: Release) {
|
||||
val download = show(
|
||||
awaitPermissionOrCancel(
|
||||
confirmText = R.string.download,
|
||||
dismissText = android.R.string.cancel,
|
||||
title = R.string.new_version_available,
|
||||
@@ -148,9 +148,7 @@ suspend fun DialogState.showNewVersion(context: Context, release: Release) {
|
||||
}
|
||||
}
|
||||
// TODO: Download in the background and show progress in notification
|
||||
if (download) {
|
||||
val file = File(AppConfig.tempDir, "update.apk").apply { delete() }
|
||||
AppUpdater.downloadUpdate(release.downloadLink, file)
|
||||
withUIContext { context.installPackage(file) }
|
||||
}
|
||||
val file = File(AppConfig.tempDir, "update.apk").apply { delete() }
|
||||
AppUpdater.downloadUpdate(release.downloadLink, file)
|
||||
withUIContext { context.installPackage(file) }
|
||||
}
|
||||
@@ -38,7 +38,7 @@ fun PrivacyScreen() {
|
||||
val coroutineScope = rememberCoroutineScope { Dispatchers.IO }
|
||||
fun launchSnackBar(content: String) = coroutineScope.launch { snackbarHostState.showSnackbar(content) }
|
||||
val dialogState = rememberDialogState()
|
||||
dialogState.Handler()
|
||||
dialogState.Intercept()
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
@@ -79,15 +79,13 @@ fun PrivacyScreen() {
|
||||
summary = stringResource(id = R.string.clear_search_history_summary),
|
||||
) {
|
||||
coroutineScope.launch {
|
||||
val confirmed = dialogState.show(
|
||||
dialogState.awaitPermissionOrCancel(
|
||||
confirmText = R.string.clear_all,
|
||||
dismissText = android.R.string.cancel,
|
||||
title = R.string.clear_search_history_confirm,
|
||||
)
|
||||
if (confirmed) {
|
||||
searchDatabase.searchDao().clear()
|
||||
launchSnackBar(searchHistoryCleared)
|
||||
}
|
||||
searchDatabase.searchDao().clear()
|
||||
launchSnackBar(searchHistoryCleared)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.CancellableContinuation
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@@ -57,106 +58,90 @@ fun interface DismissDialogScope<R> {
|
||||
}
|
||||
|
||||
class DialogState {
|
||||
private var content: (@Composable () -> Unit)? by mutableStateOf(null)
|
||||
var content: (@Composable () -> Unit)? by mutableStateOf(null)
|
||||
|
||||
@Composable
|
||||
fun Handler() = content?.invoke()
|
||||
fun Intercept() = content?.invoke()
|
||||
|
||||
fun dismiss() {
|
||||
content = null
|
||||
}
|
||||
|
||||
suspend inline fun <R> dialog(crossinline block: @Composable (CancellableContinuation<R>) -> Unit) = suspendCancellableCoroutine<R> { cont ->
|
||||
cont.invokeOnCancellation { dismiss() }
|
||||
content = { block(cont) }
|
||||
}
|
||||
|
||||
suspend fun <R> show(initial: R, @StringRes title: Int? = null, block: @Composable DialogScope<R>.() -> Unit): R {
|
||||
return suspendCancellableCoroutine { cont ->
|
||||
cont.invokeOnCancellation { dismiss() }
|
||||
content = {
|
||||
val state = remember(cont) { mutableStateOf(initial) }
|
||||
val impl = remember(cont) { object : DialogScope<R> { override var expectedValue by state } }
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
cont.cancel()
|
||||
return dialog { cont ->
|
||||
val state = remember(cont) { mutableStateOf(initial) }
|
||||
val impl = remember(cont) { object : DialogScope<R> { override var expectedValue by state } }
|
||||
AlertDialog(
|
||||
onDismissRequest = { cont.cancel() },
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
dismiss()
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
dismiss()
|
||||
cont.resume(state.value)
|
||||
}) {
|
||||
Text(text = stringResource(id = android.R.string.ok))
|
||||
}
|
||||
},
|
||||
title = title?.let { { Text(text = stringResource(id = title)) } },
|
||||
text = { block(impl) },
|
||||
)
|
||||
}
|
||||
cont.resume(state.value)
|
||||
}) {
|
||||
Text(text = stringResource(id = android.R.string.ok))
|
||||
}
|
||||
},
|
||||
title = title?.let { { Text(text = stringResource(id = title)) } },
|
||||
text = { block(impl) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun show(
|
||||
suspend fun awaitPermissionOrCancel(
|
||||
@StringRes confirmText: Int? = null,
|
||||
@StringRes dismissText: Int? = null,
|
||||
@StringRes title: Int? = null,
|
||||
text: (@Composable () -> Unit)? = null,
|
||||
): Boolean {
|
||||
return suspendCancellableCoroutine { cont ->
|
||||
cont.invokeOnCancellation { dismiss() }
|
||||
content = {
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
) {
|
||||
return dialog { cont ->
|
||||
AlertDialog(
|
||||
onDismissRequest = { cont.cancel() },
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
dismiss()
|
||||
cont.resume(false)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
dismiss()
|
||||
cont.resume(true)
|
||||
}) {
|
||||
Text(text = stringResource(id = confirmText ?: android.R.string.ok))
|
||||
cont.resume(Unit)
|
||||
}) {
|
||||
Text(text = stringResource(id = confirmText ?: android.R.string.ok))
|
||||
}
|
||||
},
|
||||
dismissButton = dismissText?.let {
|
||||
{
|
||||
TextButton(onClick = { cont.cancel() }) {
|
||||
Text(text = stringResource(id = dismissText))
|
||||
}
|
||||
},
|
||||
dismissButton = dismissText?.let {
|
||||
{
|
||||
TextButton(onClick = {
|
||||
dismiss()
|
||||
cont.resume(false)
|
||||
}) {
|
||||
Text(text = stringResource(id = dismissText))
|
||||
}
|
||||
}
|
||||
},
|
||||
title = title?.let { { Text(text = stringResource(id = title)) } },
|
||||
text = text,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
title = title?.let { { Text(text = stringResource(id = title)) } },
|
||||
text = text,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <R> showNoButton(respectDefaultWidth: Boolean = true, block: @Composable DismissDialogScope<R>.() -> Unit): R {
|
||||
return suspendCancellableCoroutine { cont ->
|
||||
cont.invokeOnCancellation { dismiss() }
|
||||
content = {
|
||||
val impl = remember(cont) {
|
||||
DismissDialogScope<R> {
|
||||
dismiss()
|
||||
cont.resume(it)
|
||||
}
|
||||
return dialog { cont ->
|
||||
val impl = remember(cont) {
|
||||
DismissDialogScope<R> {
|
||||
dismiss()
|
||||
cont.resume(it)
|
||||
}
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
cont.cancel()
|
||||
dismiss()
|
||||
},
|
||||
content = {
|
||||
Surface(
|
||||
modifier = with(Modifier) { if (!respectDefaultWidth) defaultMinSize(280.dp) else width(280.dp) },
|
||||
shape = AlertDialogDefaults.shape,
|
||||
color = AlertDialogDefaults.containerColor,
|
||||
tonalElevation = AlertDialogDefaults.TonalElevation,
|
||||
content = { block(impl) },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
AlertDialog(
|
||||
onDismissRequest = { cont.cancel() },
|
||||
content = {
|
||||
Surface(
|
||||
modifier = with(Modifier) { if (!respectDefaultWidth) defaultMinSize(280.dp) else width(280.dp) },
|
||||
shape = AlertDialogDefaults.shape,
|
||||
color = AlertDialogDefaults.containerColor,
|
||||
tonalElevation = AlertDialogDefaults.TonalElevation,
|
||||
content = { block(impl) },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user