Refactor onboarding steps

(cherry picked from commit 2ca3ab077192a7e5e2e7a5fb00c303a5a633372e)
pull/10222/head
Ivan Iskandar 9 months ago committed by arkon
parent e36a2c68f1
commit 65e1e2cf4f

@ -17,34 +17,38 @@ import eu.kanade.presentation.theme.TachiyomiTheme
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@Composable internal class GuidesStep(
internal fun GuidesStep( private val onRestoreBackup: () -> Unit,
onRestoreBackup: () -> Unit, ) : OnboardingStep {
) { override val isComplete: Boolean = true
val handler = LocalUriHandler.current
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(stringResource(MR.strings.onboarding_guides_new_user, stringResource(MR.strings.app_name)))
Button(
modifier = Modifier.fillMaxWidth(),
onClick = { handler.openUri(GETTING_STARTED_URL) },
) {
Text(stringResource(MR.strings.getting_started_guide))
}
HorizontalDivider( @Composable
color = MaterialTheme.colorScheme.onPrimaryContainer, override fun Content() {
) val handler = LocalUriHandler.current
Text(stringResource(MR.strings.onboarding_guides_returning_user, stringResource(MR.strings.app_name))) Column(
Button( modifier = Modifier.padding(16.dp),
modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp),
onClick = onRestoreBackup,
) { ) {
Text(stringResource(MR.strings.pref_restore_backup)) Text(stringResource(MR.strings.onboarding_guides_new_user, stringResource(MR.strings.app_name)))
Button(
modifier = Modifier.fillMaxWidth(),
onClick = { handler.openUri(GETTING_STARTED_URL) },
) {
Text(stringResource(MR.strings.getting_started_guide))
}
HorizontalDivider(
color = MaterialTheme.colorScheme.onPrimaryContainer,
)
Text(stringResource(MR.strings.onboarding_guides_returning_user, stringResource(MR.strings.app_name)))
Button(
modifier = Modifier.fillMaxWidth(),
onClick = onRestoreBackup,
) {
Text(stringResource(MR.strings.pref_restore_backup))
}
} }
} }
} }
@ -57,6 +61,6 @@ private fun GuidesStepPreview() {
TachiyomiTheme { TachiyomiTheme {
GuidesStep( GuidesStep(
onRestoreBackup = {}, onRestoreBackup = {},
) ).Content()
} }
} }

@ -13,15 +13,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.tachiyomi.util.system.toast
import soup.compose.material.motion.animation.materialSharedAxisX import soup.compose.material.motion.animation.materialSharedAxisX
import soup.compose.material.motion.animation.rememberSlideDistance import soup.compose.material.motion.animation.rememberSlideDistance
import tachiyomi.domain.storage.service.StoragePreferences
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@ -29,24 +26,21 @@ import tachiyomi.presentation.core.screens.InfoScreen
@Composable @Composable
fun OnboardingScreen( fun OnboardingScreen(
storagePreferences: StoragePreferences,
uiPreferences: UiPreferences,
onComplete: () -> Unit, onComplete: () -> Unit,
onRestoreBackup: () -> Unit, onRestoreBackup: () -> Unit,
) { ) {
val context = LocalContext.current
val slideDistance = rememberSlideDistance() val slideDistance = rememberSlideDistance()
var currentStep by remember { mutableIntStateOf(0) } var currentStep by rememberSaveable { mutableIntStateOf(0) }
val steps: List<@Composable () -> Unit> = remember { val steps = remember {
listOf( listOf(
{ ThemeStep(uiPreferences = uiPreferences) }, ThemeStep(),
{ StorageStep(storagePref = storagePreferences.baseStorageDirectory()) }, StorageStep(),
// TODO: prompt for notification permissions when bumping target to Android 13 // TODO: prompt for notification permissions when bumping target to Android 13
{ GuidesStep(onRestoreBackup = onRestoreBackup) }, GuidesStep(onRestoreBackup = onRestoreBackup),
) )
} }
val isLastStep = currentStep == steps.size - 1 val isLastStep = currentStep == steps.lastIndex
BackHandler(enabled = currentStep != 0, onBack = { currentStep-- }) BackHandler(enabled = currentStep != 0, onBack = { currentStep-- })
@ -61,16 +55,12 @@ fun OnboardingScreen(
MR.strings.onboarding_action_next MR.strings.onboarding_action_next
}, },
), ),
canAccept = steps[currentStep].isComplete,
onAcceptClick = { onAcceptClick = {
if (isLastStep) { if (isLastStep) {
onComplete() onComplete()
} else { } else {
// TODO: this is kind of janky currentStep++
if (currentStep == 1 && !storagePreferences.baseStorageDirectory().isSet()) {
context.toast(MR.strings.onboarding_storage_selection_required)
} else {
currentStep++
}
} }
}, },
) { ) {
@ -91,7 +81,7 @@ fun OnboardingScreen(
}, },
label = "stepContent", label = "stepContent",
) { ) {
steps[it]() steps[it].Content()
} }
} }
} }

@ -0,0 +1,11 @@
package eu.kanade.presentation.more.onboarding
import androidx.compose.runtime.Composable
internal interface OnboardingStep {
val isComplete: Boolean
@Composable
fun Content()
}

@ -7,46 +7,66 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.presentation.more.settings.screen.SettingsDataScreen import eu.kanade.presentation.more.settings.screen.SettingsDataScreen
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import tachiyomi.core.preference.Preference import kotlinx.coroutines.flow.collectLatest
import tachiyomi.domain.storage.service.StoragePreferences
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Button import tachiyomi.presentation.core.components.material.Button
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Composable internal class StorageStep : OnboardingStep {
internal fun StorageStep(
storagePref: Preference<String>, private val storagePref = Injekt.get<StoragePreferences>().baseStorageDirectory()
) {
val context = LocalContext.current private var _isComplete by mutableStateOf(false)
val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref)
override val isComplete: Boolean
Column( get() = _isComplete
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp), @Composable
) { override fun Content() {
Text( val context = LocalContext.current
stringResource( val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref)
MR.strings.onboarding_storage_info,
stringResource(MR.strings.app_name), Column(
SettingsDataScreen.storageLocationText(storagePref), modifier = Modifier.padding(16.dp),
), verticalArrangement = Arrangement.spacedBy(8.dp),
)
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {
try {
pickStorageLocation.launch(null)
} catch (e: ActivityNotFoundException) {
context.toast(MR.strings.file_picker_error)
}
},
) { ) {
Text(stringResource(MR.strings.onboarding_storage_action_select)) Text(
stringResource(
MR.strings.onboarding_storage_info,
stringResource(MR.strings.app_name),
SettingsDataScreen.storageLocationText(storagePref),
),
)
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {
try {
pickStorageLocation.launch(null)
} catch (e: ActivityNotFoundException) {
context.toast(MR.strings.file_picker_error)
}
},
) {
Text(stringResource(MR.strings.onboarding_storage_action_select))
}
}
LaunchedEffect(Unit) {
storagePref.changes()
.collectLatest { _isComplete = storagePref.isSet() }
} }
} }
} }

@ -8,33 +8,40 @@ import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
import eu.kanade.presentation.more.settings.widget.AppThemeModePreferenceWidget import eu.kanade.presentation.more.settings.widget.AppThemeModePreferenceWidget
import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Composable internal class ThemeStep : OnboardingStep {
internal fun ThemeStep(
uiPreferences: UiPreferences, override val isComplete: Boolean = true
) {
val themeModePref = uiPreferences.themeMode() private val uiPreferences: UiPreferences = Injekt.get()
val themeMode by themeModePref.collectAsState()
@Composable
val appThemePref = uiPreferences.appTheme() override fun Content() {
val appTheme by appThemePref.collectAsState() val themeModePref = uiPreferences.themeMode()
val themeMode by themeModePref.collectAsState()
val amoledPref = uiPreferences.themeDarkAmoled()
val amoled by amoledPref.collectAsState() val appThemePref = uiPreferences.appTheme()
val appTheme by appThemePref.collectAsState()
Column {
AppThemeModePreferenceWidget( val amoledPref = uiPreferences.themeDarkAmoled()
value = themeMode, val amoled by amoledPref.collectAsState()
onItemClick = {
themeModePref.set(it) Column {
setAppCompatDelegateThemeMode(it) AppThemeModePreferenceWidget(
}, value = themeMode,
) onItemClick = {
themeModePref.set(it)
AppThemePreferenceWidget( setAppCompatDelegateThemeMode(it)
value = appTheme, },
amoled = amoled, )
onItemClick = { appThemePref.set(it) },
) AppThemePreferenceWidget(
value = appTheme,
amoled = amoled,
onItemClick = { appThemePref.set(it) },
)
}
} }
} }

@ -349,7 +349,7 @@ class MainActivity : BaseActivity() {
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
if (!preferences.shownOnboardingFlow().get()) { if (!preferences.shownOnboardingFlow().get() && navigator.lastItem !is OnboardingScreen) {
navigator.push(OnboardingScreen()) navigator.push(OnboardingScreen())
} }
} }

@ -5,11 +5,9 @@ import androidx.compose.runtime.remember
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.more.onboarding.OnboardingScreen import eu.kanade.presentation.more.onboarding.OnboardingScreen
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.setting.SettingsScreen import eu.kanade.tachiyomi.ui.setting.SettingsScreen
import tachiyomi.domain.storage.service.StoragePreferences
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -20,8 +18,6 @@ class OnboardingScreen : Screen() {
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val basePreferences = remember { Injekt.get<BasePreferences>() } val basePreferences = remember { Injekt.get<BasePreferences>() }
val storagePreferences = remember { Injekt.get<StoragePreferences>() }
val uiPreferences = remember { Injekt.get<UiPreferences>() }
val finishOnboarding = { val finishOnboarding = {
basePreferences.shownOnboardingFlow().set(true) basePreferences.shownOnboardingFlow().set(true)
@ -29,8 +25,6 @@ class OnboardingScreen : Screen() {
} }
OnboardingScreen( OnboardingScreen(
storagePreferences = storagePreferences,
uiPreferences = uiPreferences,
onComplete = { finishOnboarding() }, onComplete = { finishOnboarding() },
onRestoreBackup = { onRestoreBackup = {
finishOnboarding() finishOnboarding()

@ -13,6 +13,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Newspaper import androidx.compose.material.icons.outlined.Newspaper
import androidx.compose.material3.Button
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBarDefaults import androidx.compose.material3.NavigationBarDefaults
@ -38,6 +39,7 @@ fun InfoScreen(
subtitleText: String, subtitleText: String,
acceptText: String, acceptText: String,
onAcceptClick: () -> Unit, onAcceptClick: () -> Unit,
canAccept: Boolean = true,
rejectText: String? = null, rejectText: String? = null,
onRejectClick: (() -> Unit)? = null, onRejectClick: (() -> Unit)? = null,
content: @Composable ColumnScope.() -> Unit, content: @Composable ColumnScope.() -> Unit,
@ -63,8 +65,9 @@ fun InfoScreen(
vertical = MaterialTheme.padding.small, vertical = MaterialTheme.padding.small,
), ),
) { ) {
androidx.compose.material3.Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
enabled = canAccept,
onClick = onAcceptClick, onClick = onAcceptClick,
) { ) {
Text(text = acceptText) Text(text = acceptText)

Loading…
Cancel
Save