Refactor onboarding steps
(cherry picked from commit 2ca3ab077192a7e5e2e7a5fb00c303a5a633372e)
This commit is contained in:
parent
e36a2c68f1
commit
65e1e2cf4f
8 changed files with 133 additions and 104 deletions
|
@ -17,34 +17,38 @@ import eu.kanade.presentation.theme.TachiyomiTheme
|
|||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
|
||||
@Composable
|
||||
internal fun GuidesStep(
|
||||
onRestoreBackup: () -> Unit,
|
||||
) {
|
||||
val handler = LocalUriHandler.current
|
||||
internal class GuidesStep(
|
||||
private val onRestoreBackup: () -> Unit,
|
||||
) : OnboardingStep {
|
||||
override val isComplete: Boolean = true
|
||||
|
||||
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) },
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val handler = LocalUriHandler.current
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(stringResource(MR.strings.getting_started_guide))
|
||||
}
|
||||
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,
|
||||
)
|
||||
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))
|
||||
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 {
|
||||
GuidesStep(
|
||||
onRestoreBackup = {},
|
||||
)
|
||||
).Content()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,15 +13,12 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
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.rememberSlideDistance
|
||||
import tachiyomi.domain.storage.service.StoragePreferences
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
|
@ -29,24 +26,21 @@ import tachiyomi.presentation.core.screens.InfoScreen
|
|||
|
||||
@Composable
|
||||
fun OnboardingScreen(
|
||||
storagePreferences: StoragePreferences,
|
||||
uiPreferences: UiPreferences,
|
||||
onComplete: () -> Unit,
|
||||
onRestoreBackup: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val slideDistance = rememberSlideDistance()
|
||||
|
||||
var currentStep by remember { mutableIntStateOf(0) }
|
||||
val steps: List<@Composable () -> Unit> = remember {
|
||||
var currentStep by rememberSaveable { mutableIntStateOf(0) }
|
||||
val steps = remember {
|
||||
listOf(
|
||||
{ ThemeStep(uiPreferences = uiPreferences) },
|
||||
{ StorageStep(storagePref = storagePreferences.baseStorageDirectory()) },
|
||||
ThemeStep(),
|
||||
StorageStep(),
|
||||
// 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-- })
|
||||
|
||||
|
@ -61,16 +55,12 @@ fun OnboardingScreen(
|
|||
MR.strings.onboarding_action_next
|
||||
},
|
||||
),
|
||||
canAccept = steps[currentStep].isComplete,
|
||||
onAcceptClick = {
|
||||
if (isLastStep) {
|
||||
onComplete()
|
||||
} else {
|
||||
// TODO: this is kind of janky
|
||||
if (currentStep == 1 && !storagePreferences.baseStorageDirectory().isSet()) {
|
||||
context.toast(MR.strings.onboarding_storage_selection_required)
|
||||
} else {
|
||||
currentStep++
|
||||
}
|
||||
currentStep++
|
||||
}
|
||||
},
|
||||
) {
|
||||
|
@ -91,7 +81,7 @@ fun OnboardingScreen(
|
|||
},
|
||||
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.material3.Text
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.more.settings.screen.SettingsDataScreen
|
||||
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.presentation.core.components.material.Button
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@Composable
|
||||
internal fun StorageStep(
|
||||
storagePref: Preference<String>,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref)
|
||||
internal class StorageStep : OnboardingStep {
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(
|
||||
stringResource(
|
||||
MR.strings.onboarding_storage_info,
|
||||
stringResource(MR.strings.app_name),
|
||||
SettingsDataScreen.storageLocationText(storagePref),
|
||||
),
|
||||
)
|
||||
private val storagePref = Injekt.get<StoragePreferences>().baseStorageDirectory()
|
||||
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
try {
|
||||
pickStorageLocation.launch(null)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
context.toast(MR.strings.file_picker_error)
|
||||
}
|
||||
},
|
||||
private var _isComplete by mutableStateOf(false)
|
||||
|
||||
override val isComplete: Boolean
|
||||
get() = _isComplete
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val context = LocalContext.current
|
||||
val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
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.AppThemePreferenceWidget
|
||||
import tachiyomi.presentation.core.util.collectAsState
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@Composable
|
||||
internal fun ThemeStep(
|
||||
uiPreferences: UiPreferences,
|
||||
) {
|
||||
val themeModePref = uiPreferences.themeMode()
|
||||
val themeMode by themeModePref.collectAsState()
|
||||
internal class ThemeStep : OnboardingStep {
|
||||
|
||||
val appThemePref = uiPreferences.appTheme()
|
||||
val appTheme by appThemePref.collectAsState()
|
||||
override val isComplete: Boolean = true
|
||||
|
||||
val amoledPref = uiPreferences.themeDarkAmoled()
|
||||
val amoled by amoledPref.collectAsState()
|
||||
private val uiPreferences: UiPreferences = Injekt.get()
|
||||
|
||||
Column {
|
||||
AppThemeModePreferenceWidget(
|
||||
value = themeMode,
|
||||
onItemClick = {
|
||||
themeModePref.set(it)
|
||||
setAppCompatDelegateThemeMode(it)
|
||||
},
|
||||
)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val themeModePref = uiPreferences.themeMode()
|
||||
val themeMode by themeModePref.collectAsState()
|
||||
|
||||
AppThemePreferenceWidget(
|
||||
value = appTheme,
|
||||
amoled = amoled,
|
||||
onItemClick = { appThemePref.set(it) },
|
||||
)
|
||||
val appThemePref = uiPreferences.appTheme()
|
||||
val appTheme by appThemePref.collectAsState()
|
||||
|
||||
val amoledPref = uiPreferences.themeDarkAmoled()
|
||||
val amoled by amoledPref.collectAsState()
|
||||
|
||||
Column {
|
||||
AppThemeModePreferenceWidget(
|
||||
value = themeMode,
|
||||
onItemClick = {
|
||||
themeModePref.set(it)
|
||||
setAppCompatDelegateThemeMode(it)
|
||||
},
|
||||
)
|
||||
|
||||
AppThemePreferenceWidget(
|
||||
value = appTheme,
|
||||
amoled = amoled,
|
||||
onItemClick = { appThemePref.set(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -349,7 +349,7 @@ class MainActivity : BaseActivity() {
|
|||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (!preferences.shownOnboardingFlow().get()) {
|
||||
if (!preferences.shownOnboardingFlow().get() && navigator.lastItem !is OnboardingScreen) {
|
||||
navigator.push(OnboardingScreen())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,9 @@ import androidx.compose.runtime.remember
|
|||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.presentation.more.onboarding.OnboardingScreen
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsScreen
|
||||
import tachiyomi.domain.storage.service.StoragePreferences
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
|
@ -20,8 +18,6 @@ class OnboardingScreen : Screen() {
|
|||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
val basePreferences = remember { Injekt.get<BasePreferences>() }
|
||||
val storagePreferences = remember { Injekt.get<StoragePreferences>() }
|
||||
val uiPreferences = remember { Injekt.get<UiPreferences>() }
|
||||
|
||||
val finishOnboarding = {
|
||||
basePreferences.shownOnboardingFlow().set(true)
|
||||
|
@ -29,8 +25,6 @@ class OnboardingScreen : Screen() {
|
|||
}
|
||||
|
||||
OnboardingScreen(
|
||||
storagePreferences = storagePreferences,
|
||||
uiPreferences = uiPreferences,
|
||||
onComplete = { finishOnboarding() },
|
||||
onRestoreBackup = {
|
||||
finishOnboarding()
|
||||
|
|
|
@ -13,6 +13,7 @@ import androidx.compose.foundation.rememberScrollState
|
|||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Newspaper
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.NavigationBarDefaults
|
||||
|
@ -38,6 +39,7 @@ fun InfoScreen(
|
|||
subtitleText: String,
|
||||
acceptText: String,
|
||||
onAcceptClick: () -> Unit,
|
||||
canAccept: Boolean = true,
|
||||
rejectText: String? = null,
|
||||
onRejectClick: (() -> Unit)? = null,
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
|
@ -63,8 +65,9 @@ fun InfoScreen(
|
|||
vertical = MaterialTheme.padding.small,
|
||||
),
|
||||
) {
|
||||
androidx.compose.material3.Button(
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = canAccept,
|
||||
onClick = onAcceptClick,
|
||||
) {
|
||||
Text(text = acceptText)
|
||||
|
|
Reference in a new issue