More onboarding screen additions
This commit is contained in:
parent
8b57169e92
commit
e3404cd3d3
@ -2,7 +2,7 @@ package eu.kanade.presentation.crash
|
|||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.BugReport
|
import androidx.compose.material.icons.outlined.BugReport
|
||||||
@ -47,7 +47,7 @@ fun CrashScreen(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(vertical = MaterialTheme.padding.small)
|
.padding(vertical = MaterialTheme.padding.small)
|
||||||
.clip(MaterialTheme.shapes.small)
|
.clip(MaterialTheme.shapes.small)
|
||||||
.fillMaxWidth()
|
.fillMaxSize()
|
||||||
.background(MaterialTheme.colorScheme.surfaceVariant),
|
.background(MaterialTheme.colorScheme.surfaceVariant),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
package eu.kanade.presentation.more.onboarding
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun GuidesStep(
|
||||||
|
onRestoreBackup: () -> Unit,
|
||||||
|
) {
|
||||||
|
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()
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const val GETTING_STARTED_URL = "https://tachiyomi.org/docs/guides/getting-started"
|
@ -1,18 +1,27 @@
|
|||||||
package eu.kanade.presentation.more.onboarding
|
package eu.kanade.presentation.more.onboarding
|
||||||
|
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.RocketLaunch
|
import androidx.compose.material.icons.outlined.RocketLaunch
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
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.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
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.domain.storage.service.StoragePreferences
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.screens.InfoScreen
|
import tachiyomi.presentation.core.screens.InfoScreen
|
||||||
|
|
||||||
@ -21,16 +30,20 @@ fun OnboardingScreen(
|
|||||||
storagePreferences: StoragePreferences,
|
storagePreferences: StoragePreferences,
|
||||||
uiPreferences: UiPreferences,
|
uiPreferences: UiPreferences,
|
||||||
onComplete: () -> Unit,
|
onComplete: () -> Unit,
|
||||||
|
onRestoreBackup: () -> Unit,
|
||||||
) {
|
) {
|
||||||
var currentStep by remember { mutableIntStateOf(0) }
|
var currentStep by remember { mutableIntStateOf(0) }
|
||||||
val steps: List<@Composable () -> Unit> = listOf(
|
val steps: List<@Composable () -> Unit> = listOf(
|
||||||
{ ThemeStep(uiPreferences = uiPreferences) },
|
{ ThemeStep(uiPreferences = uiPreferences) },
|
||||||
{ StorageStep(storagePref = storagePreferences.baseStorageDirectory()) },
|
{ StorageStep(storagePref = storagePreferences.baseStorageDirectory()) },
|
||||||
// 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) },
|
||||||
)
|
)
|
||||||
val isLastStep = currentStep == steps.size - 1
|
val isLastStep = currentStep == steps.size - 1
|
||||||
val slideDistance = rememberSlideDistance()
|
val slideDistance = rememberSlideDistance()
|
||||||
|
|
||||||
|
BackHandler(enabled = currentStep != 0, onBack = { currentStep-- })
|
||||||
|
|
||||||
InfoScreen(
|
InfoScreen(
|
||||||
icon = Icons.Outlined.RocketLaunch,
|
icon = Icons.Outlined.RocketLaunch,
|
||||||
headingText = stringResource(MR.strings.onboarding_heading),
|
headingText = stringResource(MR.strings.onboarding_heading),
|
||||||
@ -52,17 +65,25 @@ fun OnboardingScreen(
|
|||||||
rejectText = stringResource(MR.strings.onboarding_action_skip),
|
rejectText = stringResource(MR.strings.onboarding_action_skip),
|
||||||
onRejectClick = onComplete,
|
onRejectClick = onComplete,
|
||||||
) {
|
) {
|
||||||
AnimatedContent(
|
Box(
|
||||||
targetState = currentStep,
|
modifier = Modifier
|
||||||
transitionSpec = {
|
.padding(vertical = MaterialTheme.padding.small)
|
||||||
materialSharedAxisX(
|
.clip(MaterialTheme.shapes.small)
|
||||||
forward = true,
|
.fillMaxSize()
|
||||||
slideDistance = slideDistance,
|
.background(MaterialTheme.colorScheme.surfaceVariant),
|
||||||
)
|
|
||||||
},
|
|
||||||
label = "stepContent",
|
|
||||||
) {
|
) {
|
||||||
steps[it]()
|
AnimatedContent(
|
||||||
|
targetState = currentStep,
|
||||||
|
transitionSpec = {
|
||||||
|
materialSharedAxisX(
|
||||||
|
forward = true,
|
||||||
|
slideDistance = slideDistance,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = "stepContent",
|
||||||
|
) {
|
||||||
|
steps[it]()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,11 @@ package eu.kanade.presentation.more.onboarding
|
|||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
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.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
|
||||||
@ -22,11 +25,19 @@ internal fun StorageStep(
|
|||||||
val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref)
|
val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref)
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
) {
|
) {
|
||||||
Text(stringResource(MR.strings.onboarding_storage_info))
|
Text(
|
||||||
|
stringResource(
|
||||||
|
MR.strings.onboarding_storage_info,
|
||||||
|
stringResource(MR.strings.app_name),
|
||||||
|
SettingsDataScreen.storageLocationText(storagePref),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
onClick = {
|
onClick = {
|
||||||
try {
|
try {
|
||||||
pickStorageLocation.launch(null)
|
pickStorageLocation.launch(null)
|
||||||
@ -35,7 +46,7 @@ internal fun StorageStep(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
Text(SettingsDataScreen.storageLocationText(storagePref))
|
Text(stringResource(MR.strings.onboarding_storage_action_select))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,10 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val storageDir by storageDirPref.collectAsState()
|
val storageDir by storageDirPref.collectAsState()
|
||||||
|
|
||||||
|
if (storageDir == storageDirPref.defaultValue()) {
|
||||||
|
return stringResource(MR.strings.no_location_set)
|
||||||
|
}
|
||||||
|
|
||||||
return remember(storageDir) {
|
return remember(storageDir) {
|
||||||
val file = UniFile.fromUri(context, storageDir.toUri())
|
val file = UniFile.fromUri(context, storageDir.toUri())
|
||||||
file?.filePath ?: file?.uri?.toString()
|
file?.filePath ?: file?.uri?.toString()
|
||||||
|
@ -33,6 +33,7 @@ import eu.kanade.presentation.library.LibrarySettingsDialog
|
|||||||
import eu.kanade.presentation.library.components.LibraryContent
|
import eu.kanade.presentation.library.components.LibraryContent
|
||||||
import eu.kanade.presentation.library.components.LibraryToolbar
|
import eu.kanade.presentation.library.components.LibraryToolbar
|
||||||
import eu.kanade.presentation.manga.components.LibraryBottomActionMenu
|
import eu.kanade.presentation.manga.components.LibraryBottomActionMenu
|
||||||
|
import eu.kanade.presentation.more.onboarding.GETTING_STARTED_URL
|
||||||
import eu.kanade.presentation.util.Tab
|
import eu.kanade.presentation.util.Tab
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||||
@ -163,7 +164,7 @@ object LibraryTab : Tab {
|
|||||||
EmptyScreenAction(
|
EmptyScreenAction(
|
||||||
stringRes = MR.strings.getting_started_guide,
|
stringRes = MR.strings.getting_started_guide,
|
||||||
icon = Icons.AutoMirrored.Outlined.HelpOutline,
|
icon = Icons.AutoMirrored.Outlined.HelpOutline,
|
||||||
onClick = { handler.openUri("https://tachiyomi.org/docs/guides/getting-started") },
|
onClick = { handler.openUri(GETTING_STARTED_URL) },
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -8,6 +8,7 @@ import eu.kanade.domain.base.BasePreferences
|
|||||||
import eu.kanade.domain.ui.UiPreferences
|
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 tachiyomi.domain.storage.service.StoragePreferences
|
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
|
||||||
@ -22,12 +23,18 @@ class OnboardingScreen : Screen() {
|
|||||||
val storagePreferences = remember { Injekt.get<StoragePreferences>() }
|
val storagePreferences = remember { Injekt.get<StoragePreferences>() }
|
||||||
val uiPreferences = remember { Injekt.get<UiPreferences>() }
|
val uiPreferences = remember { Injekt.get<UiPreferences>() }
|
||||||
|
|
||||||
|
val finishOnboarding = {
|
||||||
|
basePreferences.shownOnboardingFlow().set(true)
|
||||||
|
navigator.pop()
|
||||||
|
}
|
||||||
|
|
||||||
OnboardingScreen(
|
OnboardingScreen(
|
||||||
storagePreferences = storagePreferences,
|
storagePreferences = storagePreferences,
|
||||||
uiPreferences = uiPreferences,
|
uiPreferences = uiPreferences,
|
||||||
onComplete = {
|
onComplete = { finishOnboarding() },
|
||||||
basePreferences.shownOnboardingFlow().set(true)
|
onRestoreBackup = {
|
||||||
navigator.pop()
|
finishOnboarding()
|
||||||
|
navigator.push(SettingsScreen.toDataAndStorageScreen())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,10 @@
|
|||||||
<string name="onboarding_action_next">Next</string>
|
<string name="onboarding_action_next">Next</string>
|
||||||
<string name="onboarding_action_finish">Get started</string>
|
<string name="onboarding_action_finish">Get started</string>
|
||||||
<string name="onboarding_action_skip">Skip</string>
|
<string name="onboarding_action_skip">Skip</string>
|
||||||
<string name="onboarding_storage_info">Select a storage location where chapter downloads, backups, and local source content will be stored.</string>
|
<string name="onboarding_storage_info">Select a folder where %1$s will store chapter downloads, backups, and more.\n\nA dedicated folder is recommended.\n\nSelected folder: %2$s</string>
|
||||||
|
<string name="onboarding_storage_action_select">Select a folder</string>
|
||||||
|
<string name="onboarding_guides_new_user">New to %s? We recommend checking out the getting started guide.</string>
|
||||||
|
<string name="onboarding_guides_returning_user">Already used %s before?</string>
|
||||||
|
|
||||||
<!-- Preferences -->
|
<!-- Preferences -->
|
||||||
<!-- Subsections -->
|
<!-- Subsections -->
|
||||||
@ -439,6 +442,7 @@
|
|||||||
<string name="pref_remove_after_read">After reading automatically delete</string>
|
<string name="pref_remove_after_read">After reading automatically delete</string>
|
||||||
<string name="pref_remove_bookmarked_chapters">Allow deleting bookmarked chapters</string>
|
<string name="pref_remove_bookmarked_chapters">Allow deleting bookmarked chapters</string>
|
||||||
<string name="pref_remove_exclude_categories">Excluded categories</string>
|
<string name="pref_remove_exclude_categories">Excluded categories</string>
|
||||||
|
<string name="no_location_set">No storage location set</string>
|
||||||
<string name="invalid_location">Invalid location: %s</string>
|
<string name="invalid_location">Invalid location: %s</string>
|
||||||
<string name="disabled">Disabled</string>
|
<string name="disabled">Disabled</string>
|
||||||
<string name="last_read_chapter">Last read chapter</string>
|
<string name="last_read_chapter">Last read chapter</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user