mirror of
https://github.com/null2264/yokai.git
synced 2025-06-20 18:24:42 +00:00
refactor: Rework Dialog
This commit is contained in:
parent
1a16d84e61
commit
49b10c1b4f
14 changed files with 223 additions and 130 deletions
|
@ -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,8 +30,14 @@ abstract class BaseComposeController(bundle: Bundle? = null) :
|
|||
)
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
val dialogHostState = remember { DialogHostState() }
|
||||
YokaiTheme {
|
||||
ScreenContent()
|
||||
CompositionLocalProvider(
|
||||
LocalDialogHostState provides dialogHostState,
|
||||
LocalBackPress provides router::handleBack,
|
||||
) {
|
||||
ScreenContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1014,7 +1014,12 @@ open class MainActivity : BaseActivity<MainActivityBinding>() {
|
|||
withContext(Dispatchers.Main) {
|
||||
showNotificationPermissionPrompt()
|
||||
AppUpdateNotifier.releasePageUrl = result.release.releaseLink
|
||||
AboutController.NewUpdateDialogController(body, url, isBeta).showDialog(router)
|
||||
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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
getComposableSettings().Content()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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)
|
||||
}
|
28
app/src/main/java/yokai/domain/DialogHostState.kt
Normal file
28
app/src/main/java/yokai/domain/DialogHostState.kt
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
ExtensionRepoScreen(
|
||||
title = "Extension Repos",
|
||||
repoUrl = repoUrl,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,46 +131,45 @@ 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(
|
||||
oldRepo = event.dialog.oldRepo,
|
||||
newRepo = event.dialog.newRepo,
|
||||
onDismissRequest = { alertDialog.content = null },
|
||||
onMigrate = { viewModel.replaceRepo(event.dialog.newRepo) },
|
||||
)
|
||||
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,
|
||||
onMigrate = { viewModel.replaceRepo(event.dialog.newRepo) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
},
|
||||
)
|
||||
}
|
|
@ -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 -> {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue