refactor: Rework Dialog

This commit is contained in:
Ahmad Ansori Palembani 2025-01-02 21:42:53 +07:00
parent 1a16d84e61
commit 49b10c1b4f
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
14 changed files with 223 additions and 130 deletions

View file

@ -5,8 +5,13 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import eu.kanade.tachiyomi.util.compose.LocalBackPress
import eu.kanade.tachiyomi.util.compose.LocalDialogHostState
import yokai.domain.DialogHostState
import yokai.presentation.theme.YokaiTheme
abstract class BaseComposeController(bundle: Bundle? = null) :
@ -25,12 +30,18 @@ abstract class BaseComposeController(bundle: Bundle? = null) :
)
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
val dialogHostState = remember { DialogHostState() }
YokaiTheme {
CompositionLocalProvider(
LocalDialogHostState provides dialogHostState,
LocalBackPress provides router::handleBack,
) {
ScreenContent()
}
}
}
}
}
@Composable
abstract fun ScreenContent()

View file

@ -1014,9 +1014,14 @@ open class MainActivity : BaseActivity<MainActivityBinding>() {
withContext(Dispatchers.Main) {
showNotificationPermissionPrompt()
AppUpdateNotifier.releasePageUrl = result.release.releaseLink
if (
// FIXME: Show Compose version of NewUpdateDialog for AboutController
router.backstack.lastOrNull()?.controller !is AboutController
) {
AboutController.NewUpdateDialogController(body, url, isBeta).showDialog(router)
}
}
}
} catch (error: Exception) {
Logger.e(error)
}
@ -1037,6 +1042,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>() {
@SuppressLint("MissingSuperCall")
override fun onNewIntent(intent: Intent) {
splashState.ready = true
if (!handleIntentAction(intent)) {
super.onNewIntent(intent)
}
@ -1092,7 +1098,11 @@ open class MainActivity : BaseActivity<MainActivityBinding>() {
SHORTCUT_UPDATE_NOTES -> {
val extras = intent.extras ?: return false
if (router.backstack.isEmpty()) nav.selectedItemId = R.id.nav_library
if (router.backstack.lastOrNull()?.controller !is AboutController.NewUpdateDialogController) {
if (
router.backstack.lastOrNull()?.controller !is AboutController.NewUpdateDialogController &&
// FIXME: Show Compose version of NewUpdateDialog for AboutController
router.backstack.lastOrNull()?.controller !is AboutController
) {
AboutController.NewUpdateDialogController(extras).showDialog(router)
}
}
@ -1122,7 +1132,6 @@ open class MainActivity : BaseActivity<MainActivityBinding>() {
else -> return false
}
splashState.ready = true
return true
}

View file

@ -1,28 +1,24 @@
package eu.kanade.tachiyomi.ui.more
import android.app.Dialog
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.text.Spanned
import android.text.method.LinkMovementMethod
import android.view.View
import android.widget.TextView
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import cafe.adriel.voyager.core.stack.StackEvent
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.CrossfadeTransition
import eu.kanade.tachiyomi.data.updater.AppDownloadInstallJob
import eu.kanade.tachiyomi.ui.base.controller.BaseComposeController
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.compose.LocalAlertDialog
import eu.kanade.tachiyomi.util.compose.LocalBackPress
import eu.kanade.tachiyomi.util.compose.LocalRouter
import eu.kanade.tachiyomi.util.system.materialAlertDialog
import eu.kanade.tachiyomi.util.view.setNegativeButton
import eu.kanade.tachiyomi.util.view.setPositiveButton
import eu.kanade.tachiyomi.util.view.setTitle
import io.noties.markwon.Markwon
import yokai.domain.ComposableAlertDialog
import yokai.i18n.MR
import yokai.presentation.settings.screen.about.AboutScreen
import android.R as AR
@ -34,17 +30,12 @@ class AboutController : BaseComposeController() {
Navigator(
screen = AboutScreen(),
content = {
CompositionLocalProvider(
LocalAlertDialog provides ComposableAlertDialog(null),
LocalBackPress provides router::handleBack,
LocalRouter provides router,
) {
CrossfadeTransition(navigator = it)
}
},
)
}
@Deprecated("Use [DialogHostState.showNewUpdateDialog] instead", ReplaceWith("DialogHostState.showNewUpdateDialog()"))
class NewUpdateDialogController(bundle: Bundle? = null) : DialogController(bundle) {
constructor(body: String, url: String, isBeta: Boolean?) : this(
@ -56,9 +47,7 @@ class AboutController : BaseComposeController() {
)
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val releaseBody = (args.getString(BODY_KEY) ?: "")
.replace("""---(\R|.)*Checksums(\R|.)*""".toRegex(), "")
val info = Markwon.create(activity!!).toMarkdown(releaseBody)
val info = activity!!.parseReleaseNotes(args.getString(BODY_KEY) ?: "")
val isOnA12 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val isBeta = args.getBoolean(IS_BETA, false)
@ -96,3 +85,8 @@ class AboutController : BaseComposeController() {
}
}
}
fun Context.parseReleaseNotes(releaseNotes: String): Spanned {
val releaseBody = releaseNotes.replace("""---(\R|.)*Checksums(\R|.)*""".toRegex(), "")
return Markwon.create(this).toMarkdown(releaseBody)
}

View file

@ -1,11 +1,7 @@
package eu.kanade.tachiyomi.ui.setting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import eu.kanade.tachiyomi.ui.base.controller.BaseComposeController
import eu.kanade.tachiyomi.util.compose.LocalAlertDialog
import eu.kanade.tachiyomi.util.compose.LocalBackPress
import yokai.domain.ComposableAlertDialog
import yokai.presentation.settings.ComposableSettings
abstract class SettingsComposeController: BaseComposeController(), SettingsControllerInterface {
@ -18,11 +14,6 @@ abstract class SettingsComposeController: BaseComposeController(), SettingsContr
@Composable
override fun ScreenContent() {
CompositionLocalProvider(
LocalAlertDialog provides ComposableAlertDialog(null),
LocalBackPress provides router::handleBack,
) {
getComposableSettings().Content()
}
}
}

View file

@ -5,14 +5,14 @@ import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.staticCompositionLocalOf
import com.bluelinelabs.conductor.Router
import yokai.domain.ComposableAlertDialog
import yokai.domain.DialogHostState
val <T> ProvidableCompositionLocal<T?>.currentOrThrow
@Composable
get(): T = this.current ?: throw RuntimeException("CompositionLocal is null")
val LocalBackPress: ProvidableCompositionLocal<(() -> Unit)?> = staticCompositionLocalOf { null }
val LocalAlertDialog: ProvidableCompositionLocal<ComposableAlertDialog?> = compositionLocalOf { null }
val LocalDialogHostState: ProvidableCompositionLocal<DialogHostState?> = compositionLocalOf { null }
@Deprecated(
message = "Scheduled for removal once Conductor is fully replaced by Voyager",
replaceWith = ReplaceWith("LocalNavigator", "cafe.adriel.voyager.navigator.LocalNavigator"),

View file

@ -1,10 +0,0 @@
package yokai.domain
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
class ComposableAlertDialog(initial: (@Composable () -> Unit)?) {
var content: (@Composable () -> Unit)? by mutableStateOf(initial)
}

View file

@ -0,0 +1,28 @@
package yokai.domain
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
typealias ComposableDialog = (@Composable () -> Unit)?
typealias ComposableDialogState = MutableState<ComposableDialog>
class DialogHostState(initial: ComposableDialog = null) : ComposableDialogState by mutableStateOf(initial) {
val mutex = Mutex()
fun closeDialog() {
value = null
}
suspend inline fun <R> dialog(crossinline dialog: @Composable (CancellableContinuation<R>) -> Unit) = mutex.withLock {
try {
suspendCancellableCoroutine { cont -> value = { dialog(cont) } }
} finally {
closeDialog()
}
}
}

View file

@ -1,11 +1,7 @@
package yokai.presentation.extension.repo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import eu.kanade.tachiyomi.ui.base.controller.BaseComposeController
import eu.kanade.tachiyomi.util.compose.LocalAlertDialog
import eu.kanade.tachiyomi.util.compose.LocalBackPress
import yokai.domain.ComposableAlertDialog
class ExtensionRepoController() :
BaseComposeController() {
@ -18,14 +14,9 @@ class ExtensionRepoController() :
@Composable
override fun ScreenContent() {
CompositionLocalProvider(
LocalAlertDialog provides ComposableAlertDialog(null),
LocalBackPress provides router::handleBack,
) {
ExtensionRepoScreen(
title = "Extension Repos",
repoUrl = repoUrl,
)
}
}
}

View file

@ -19,6 +19,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@ -26,13 +27,14 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import dev.icerock.moko.resources.compose.stringResource
import eu.kanade.tachiyomi.util.compose.LocalAlertDialog
import eu.kanade.tachiyomi.util.compose.LocalBackPress
import eu.kanade.tachiyomi.util.compose.LocalDialogHostState
import eu.kanade.tachiyomi.util.compose.currentOrThrow
import eu.kanade.tachiyomi.util.isTablet
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest
import yokai.domain.ComposableAlertDialog
import kotlinx.coroutines.launch
import yokai.domain.DialogHostState
import yokai.domain.extension.repo.model.ExtensionRepo
import yokai.i18n.MR
import yokai.presentation.AppBarType
@ -51,10 +53,12 @@ fun ExtensionRepoScreen(
) {
val onBackPress = LocalBackPress.currentOrThrow
val context = LocalContext.current
val alertDialog = LocalDialogHostState.currentOrThrow
val scope = rememberCoroutineScope()
val repoState by viewModel.repoState.collectAsState()
var inputText by remember { mutableStateOf("") }
val listState = rememberLazyListState()
val alertDialog = LocalAlertDialog.currentOrThrow
YokaiScaffold(
onNavigationIconClicked = onBackPress,
@ -79,7 +83,7 @@ fun ExtensionRepoScreen(
val repos = (repoState as ExtensionRepoState.Success).repos
alertDialog.content?.let { it() }
alertDialog.value?.invoke()
LazyColumn(
modifier = Modifier.padding(innerPadding),
@ -113,7 +117,7 @@ fun ExtensionRepoScreen(
ExtensionRepoItem(
extensionRepo = repo,
onDeleteClick = { repoToDelete ->
alertDialog.content = { ExtensionRepoDeletePrompt(repoToDelete, alertDialog, viewModel) }
scope.launch { alertDialog.awaitExtensionRepoDeletePrompt(repoToDelete, viewModel) }
},
)
}
@ -127,17 +131,16 @@ fun ExtensionRepoScreen(
LaunchedEffect(Unit) {
viewModel.event.collectLatest { event ->
if (event is ExtensionRepoEvent.LocalizedMessage)
context.toast(event.stringRes)
if (event is ExtensionRepoEvent.Success)
inputText = ""
if (event is ExtensionRepoEvent.ShowDialog)
alertDialog.content = {
if (event.dialog is RepoDialog.Conflict) {
ExtensionRepoReplacePrompt(
when (event) {
is ExtensionRepoEvent.NoOp -> {}
is ExtensionRepoEvent.LocalizedMessage -> context.toast(event.stringRes)
is ExtensionRepoEvent.Success -> inputText = ""
is ExtensionRepoEvent.ShowDialog -> {
when(event.dialog) {
is RepoDialog.Conflict -> {
alertDialog.awaitExtensionRepoReplacePrompt(
oldRepo = event.dialog.oldRepo,
newRepo = event.dialog.newRepo,
onDismissRequest = { alertDialog.content = null },
onMigrate = { viewModel.replaceRepo(event.dialog.newRepo) },
)
}
@ -145,28 +148,28 @@ fun ExtensionRepoScreen(
}
}
}
}
}
@Composable
fun ExtensionRepoReplacePrompt(
suspend fun DialogHostState.awaitExtensionRepoReplacePrompt(
oldRepo: ExtensionRepo,
newRepo: ExtensionRepo,
onDismissRequest: () -> Unit,
onMigrate: () -> Unit,
) {
): Unit = dialog { cont ->
AlertDialog(
onDismissRequest = onDismissRequest,
onDismissRequest = { cont.cancel() },
confirmButton = {
TextButton(
onClick = {
onMigrate()
onDismissRequest()
cont.cancel()
},
) {
Text(text = stringResource(MR.strings.action_replace_repo))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
TextButton(onClick = { cont.cancel() }) {
Text(text = stringResource(AR.string.cancel))
}
},
@ -179,8 +182,10 @@ fun ExtensionRepoReplacePrompt(
)
}
@Composable
fun ExtensionRepoDeletePrompt(repoToDelete: String, alertDialog: ComposableAlertDialog, viewModel: ExtensionRepoViewModel) {
suspend fun DialogHostState.awaitExtensionRepoDeletePrompt(
repoToDelete: String,
viewModel: ExtensionRepoViewModel,
): Unit = dialog { cont ->
AlertDialog(
containerColor = MaterialTheme.colorScheme.surface,
title = {
@ -199,12 +204,12 @@ fun ExtensionRepoDeletePrompt(repoToDelete: String, alertDialog: ComposableAlert
fontSize = 14.sp,
)
},
onDismissRequest = { alertDialog.content = null },
onDismissRequest = { cont.cancel() },
confirmButton = {
TextButton(
onClick = {
viewModel.deleteRepo(repoToDelete)
alertDialog.content = null
cont.cancel()
}
) {
Text(
@ -215,7 +220,7 @@ fun ExtensionRepoDeletePrompt(repoToDelete: String, alertDialog: ComposableAlert
}
},
dismissButton = {
TextButton(onClick = { alertDialog.content = null }) {
TextButton(onClick = { cont.cancel() }) {
Text(
text = stringResource(MR.strings.cancel),
color = MaterialTheme.colorScheme.primary,

View file

@ -17,8 +17,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import eu.kanade.tachiyomi.core.storage.preference.collectAsState
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.util.compose.LocalAlertDialog
import eu.kanade.tachiyomi.util.compose.LocalBackPress
import eu.kanade.tachiyomi.util.compose.LocalDialogHostState
import eu.kanade.tachiyomi.util.compose.currentOrThrow
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.delay
@ -44,7 +44,7 @@ fun SettingsScaffold(
val preferences: PreferencesHelper by injectLazy()
val useLargeAppBar by preferences.useLargeToolbar().collectAsState()
val onBackPress = LocalBackPress.currentOrThrow
val alertDialog = LocalAlertDialog.currentOrThrow
val alertDialog = LocalDialogHostState.currentOrThrow
YokaiScaffold(
onNavigationIconClicked = onBackPress,
@ -54,7 +54,7 @@ fun SettingsScaffold(
scrollBehavior = appBarScrollBehavior,
snackbarHost = snackbarHost,
) { innerPadding ->
alertDialog.content?.let { it() }
alertDialog.value?.invoke()
content(innerPadding)
}

View file

@ -33,11 +33,10 @@ import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.util.compose.LocalAlertDialog
import eu.kanade.tachiyomi.util.compose.LocalDialogHostState
import eu.kanade.tachiyomi.util.compose.currentOrThrow
import eu.kanade.tachiyomi.util.relativeTimeSpanString
import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.e
import eu.kanade.tachiyomi.util.system.launchNonCancellableIO
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.system.withUIContext
@ -57,9 +56,9 @@ import yokai.presentation.component.preference.storageLocationText
import yokai.presentation.component.preference.widget.BasePreferenceWidget
import yokai.presentation.component.preference.widget.PrefsHorizontalPadding
import yokai.presentation.settings.ComposableSettings
import yokai.presentation.settings.screen.data.CreateBackup
import yokai.presentation.settings.screen.data.RestoreBackup
import yokai.presentation.settings.screen.data.StorageInfo
import yokai.presentation.settings.screen.data.awaitCreateBackup
import yokai.presentation.settings.screen.data.awaitRestoreBackup
import yokai.presentation.settings.screen.data.storageLocationPicker
import yokai.util.lang.getString
@ -101,7 +100,7 @@ object SettingsDataScreen : ComposableSettings {
private fun getBackupAndRestoreGroup(backupPreferences: BackupPreferences): Preference.PreferenceGroup {
val scope = rememberCoroutineScope()
val context = LocalContext.current
val alertDialog = LocalAlertDialog.currentOrThrow
val alertDialog = LocalDialogHostState.currentOrThrow
val extensionManager = remember { Injekt.get<ExtensionManager>() }
val storageManager = remember { Injekt.get<StorageManager>() }
@ -122,14 +121,11 @@ object SettingsDataScreen : ComposableSettings {
Pair(null, e)
}
alertDialog.content = {
RestoreBackup(
scope.launch {
alertDialog.awaitRestoreBackup(
context = context,
uri = it,
pair = results,
onDismissRequest = {
alertDialog.content = null
}
)
}
}
@ -166,11 +162,10 @@ object SettingsDataScreen : ComposableSettings {
return@SegmentedButton
}
alertDialog.content = {
CreateBackup(
scope.launch {
alertDialog.awaitCreateBackup(
context = context,
uri = dir.uri,
onDismissRequest = { alertDialog.content = null },
)
}
} else {

View file

@ -0,0 +1,83 @@
package yokai.presentation.settings.screen.about
import android.os.Build
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import com.google.android.material.textview.MaterialTextView
import dev.icerock.moko.resources.compose.stringResource
import eu.kanade.tachiyomi.data.updater.AppDownloadInstallJob
import eu.kanade.tachiyomi.ui.more.parseReleaseNotes
import java.io.Serializable
import yokai.domain.DialogHostState
import yokai.i18n.MR
data class NewUpdateData(
val body: String,
val url: String,
val isBeta: Boolean?,
) : Serializable
suspend fun DialogHostState.awaitNewUpdateDialog(
data: NewUpdateData,
onDismiss: () -> Unit = {},
): Unit = dialog { cont ->
val context = LocalContext.current
val appContext = context.applicationContext
val isOnA12 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
AlertDialog(
onDismissRequest = {
onDismiss()
cont.cancel()
},
title = {
Text(
text = stringResource(
if (data.isBeta == true) {
MR.strings.new_beta_version_available
} else {
MR.strings.new_version_available
}
)
)
},
confirmButton = {
TextButton(onClick = {
AppDownloadInstallJob.start(appContext, data.url, true)
onDismiss()
cont.cancel()
}) {
Text(text = stringResource(if (isOnA12) MR.strings.update else MR.strings.download))
}
},
dismissButton = {
TextButton(
onClick = {
onDismiss()
cont.cancel()
}
) {
Text(text = stringResource(MR.strings.ignore))
}
},
text = { MarkdownText(data.body) }
)
}
@Composable
private fun MarkdownText(text: String) {
val context = LocalContext.current
AndroidView(
factory = {
MaterialTextView(it)
},
update = {
it.text = context.parseReleaseNotes(text)
},
)
}

View file

@ -27,7 +27,6 @@ import androidx.compose.ui.unit.dp
import androidx.core.content.getSystemService
import cafe.adriel.voyager.navigator.LocalNavigator
import co.touchlab.kermit.Logger
import com.bluelinelabs.conductor.Router
import dev.icerock.moko.resources.compose.stringResource
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.core.storage.preference.asDateFormat
@ -36,9 +35,8 @@ import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
import eu.kanade.tachiyomi.data.updater.AppUpdateNotifier
import eu.kanade.tachiyomi.data.updater.AppUpdateResult
import eu.kanade.tachiyomi.data.updater.RELEASE_URL
import eu.kanade.tachiyomi.ui.more.AboutController.NewUpdateDialogController
import eu.kanade.tachiyomi.util.CrashLogUtil
import eu.kanade.tachiyomi.util.compose.LocalRouter
import eu.kanade.tachiyomi.util.compose.LocalDialogHostState
import eu.kanade.tachiyomi.util.compose.currentOrThrow
import eu.kanade.tachiyomi.util.lang.toTimestampString
import eu.kanade.tachiyomi.util.system.isOnline
@ -52,6 +50,7 @@ import java.util.Locale
import java.util.TimeZone
import kotlinx.coroutines.launch
import uy.kohesive.injekt.injectLazy
import yokai.domain.DialogHostState
import yokai.i18n.MR
import yokai.presentation.component.preference.widget.TextPreferenceWidget
import yokai.presentation.core.components.LinkIcon
@ -68,7 +67,7 @@ class AboutScreen : Screen() {
override fun Content() {
val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow
val router = LocalRouter.currentOrThrow
val dialogHostState = LocalDialogHostState.currentOrThrow
val uriHandler = LocalUriHandler.current
val snackbarHostState = remember { SnackbarHostState() }
@ -109,7 +108,7 @@ class AboutScreen : Screen() {
onPreferenceClick = {
if (context.isOnline()) {
scope.launch {
context.checkVersion(router)
context.checkVersion(dialogHostState)
}
} else {
context.toast(MR.strings.no_network_connection)
@ -193,7 +192,7 @@ class AboutScreen : Screen() {
else -> "Release ${BuildConfig.VERSION_NAME}"
}
private suspend fun Context.checkVersion(router: Router) {
private suspend fun Context.checkVersion(dialogState: DialogHostState) {
val updateChecker = AppUpdateChecker()
withUIContext { toast(MR.strings.searching_for_updates) }
@ -208,14 +207,16 @@ class AboutScreen : Screen() {
}
when (result) {
is AppUpdateResult.NewUpdate -> {
val body = result.release.info
val url = result.release.downloadLink
val isBeta = result.release.preRelease == true
val data = NewUpdateData(
result.release.info,
result.release.downloadLink,
result.release.preRelease == true
)
// Create confirmation window
withUIContext {
AppUpdateNotifier.releasePageUrl = result.release.releaseLink
NewUpdateDialogController(body, url, isBeta).showDialog(router)
dialogState.awaitNewUpdateDialog(data)
}
}
is AppUpdateResult.NoNewUpdate -> {

View file

@ -8,10 +8,8 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import com.hippo.unifile.UniFile
@ -22,17 +20,16 @@ import eu.kanade.tachiyomi.data.backup.create.BackupOptions
import eu.kanade.tachiyomi.data.backup.models.Backup
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
import eu.kanade.tachiyomi.util.system.toast
import yokai.domain.DialogHostState
import yokai.i18n.MR
import yokai.presentation.component.LabeledCheckbox
import android.R as AR
@Composable
fun RestoreBackup(
suspend fun DialogHostState.awaitRestoreBackup(
context: Context,
uri: Uri,
pair: Pair<Results?, Exception?>,
onDismissRequest: () -> Unit,
) {
): Unit = dialog { cont ->
val (results, e) = pair
if (results != null) {
var message = stringResource(MR.strings.restore_content_full)
@ -52,20 +49,20 @@ fun RestoreBackup(
}
AlertDialog(
onDismissRequest = onDismissRequest,
onDismissRequest = { cont.cancel() },
confirmButton = {
TextButton(
onClick = {
context.toast(MR.strings.restoring_backup)
BackupRestoreJob.start(context, uri)
onDismissRequest()
cont.cancel()
},
) {
Text(text = stringResource(MR.strings.restore))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
TextButton(onClick = { cont.cancel() }) {
Text(text = stringResource(AR.string.cancel))
}
},
@ -74,9 +71,9 @@ fun RestoreBackup(
)
} else {
AlertDialog(
onDismissRequest = onDismissRequest,
onDismissRequest = { cont.cancel() },
confirmButton = {
TextButton(onClick = onDismissRequest) {
TextButton(onClick = { cont.cancel() }) {
Text(text = stringResource(AR.string.cancel))
}
},
@ -86,29 +83,27 @@ fun RestoreBackup(
}
}
@Composable
fun CreateBackup(
suspend fun DialogHostState.awaitCreateBackup(
context: Context,
uri: Uri,
onDismissRequest: () -> Unit,
) {
var options by remember { mutableStateOf(BackupOptions()) }
): Unit = dialog { cont ->
var options by mutableStateOf(BackupOptions())
AlertDialog(
onDismissRequest = onDismissRequest,
onDismissRequest = { cont.cancel() },
confirmButton = {
TextButton(onClick = {
val actualUri =
UniFile.fromUri(context, uri)?.createFile(Backup.getBackupFilename())?.uri ?: return@TextButton
context.toast(MR.strings.creating_backup)
BackupCreatorJob.startNow(context, actualUri, options)
onDismissRequest()
cont.cancel()
}) {
Text(stringResource(MR.strings.create))
}
},
dismissButton = {
TextButton(onClick = { onDismissRequest() }) {
TextButton(onClick = { cont.cancel() }) {
Text(stringResource(MR.strings.cancel))
}
},