Эх сурвалжийг харах

More onboarding screen additions

arkon 1 жил өмнө
parent
commit
e3404cd3d3

+ 2 - 2
app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt

@@ -2,7 +2,7 @@ package eu.kanade.presentation.crash
 
 import androidx.compose.foundation.background
 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.material.icons.Icons
 import androidx.compose.material.icons.outlined.BugReport
@@ -47,7 +47,7 @@ fun CrashScreen(
             modifier = Modifier
                 .padding(vertical = MaterialTheme.padding.small)
                 .clip(MaterialTheme.shapes.small)
-                .fillMaxWidth()
+                .fillMaxSize()
                 .background(MaterialTheme.colorScheme.surfaceVariant),
         ) {
             Text(

+ 47 - 0
app/src/main/java/eu/kanade/presentation/more/onboarding/GuidesStep.kt

@@ -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"

+ 31 - 10
app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt

@@ -1,18 +1,27 @@
 package eu.kanade.presentation.more.onboarding
 
+import androidx.activity.compose.BackHandler
 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.outlined.RocketLaunch
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
 import eu.kanade.domain.ui.UiPreferences
 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
 import tachiyomi.presentation.core.screens.InfoScreen
 
@@ -21,16 +30,20 @@ fun OnboardingScreen(
     storagePreferences: StoragePreferences,
     uiPreferences: UiPreferences,
     onComplete: () -> Unit,
+    onRestoreBackup: () -> Unit,
 ) {
     var currentStep by remember { mutableIntStateOf(0) }
     val steps: List<@Composable () -> Unit> = listOf(
         { ThemeStep(uiPreferences = uiPreferences) },
         { StorageStep(storagePref = storagePreferences.baseStorageDirectory()) },
         // TODO: prompt for notification permissions when bumping target to Android 13
+        { GuidesStep(onRestoreBackup = onRestoreBackup) },
     )
     val isLastStep = currentStep == steps.size - 1
     val slideDistance = rememberSlideDistance()
 
+    BackHandler(enabled = currentStep != 0, onBack = { currentStep-- })
+
     InfoScreen(
         icon = Icons.Outlined.RocketLaunch,
         headingText = stringResource(MR.strings.onboarding_heading),
@@ -52,17 +65,25 @@ fun OnboardingScreen(
         rejectText = stringResource(MR.strings.onboarding_action_skip),
         onRejectClick = onComplete,
     ) {
-        AnimatedContent(
-            targetState = currentStep,
-            transitionSpec = {
-                materialSharedAxisX(
-                    forward = true,
-                    slideDistance = slideDistance,
-                )
-            },
-            label = "stepContent",
+        Box(
+            modifier = Modifier
+                .padding(vertical = MaterialTheme.padding.small)
+                .clip(MaterialTheme.shapes.small)
+                .fillMaxSize()
+                .background(MaterialTheme.colorScheme.surfaceVariant),
         ) {
-            steps[it]()
+            AnimatedContent(
+                targetState = currentStep,
+                transitionSpec = {
+                    materialSharedAxisX(
+                        forward = true,
+                        slideDistance = slideDistance,
+                    )
+                },
+                label = "stepContent",
+            ) {
+                steps[it]()
+            }
         }
     }
 }

+ 13 - 2
app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt

@@ -3,8 +3,11 @@ package eu.kanade.presentation.more.onboarding
 import android.content.ActivityNotFoundException
 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.Text
 import androidx.compose.runtime.Composable
+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
@@ -22,11 +25,19 @@ internal fun StorageStep(
     val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref)
 
     Column(
+        modifier = Modifier.padding(16.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(
+            modifier = Modifier.fillMaxWidth(),
             onClick = {
                 try {
                     pickStorageLocation.launch(null)
@@ -35,7 +46,7 @@ internal fun StorageStep(
                 }
             },
         ) {
-            Text(SettingsDataScreen.storageLocationText(storagePref))
+            Text(stringResource(MR.strings.onboarding_storage_action_select))
         }
     }
 }

+ 4 - 0
app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt

@@ -110,6 +110,10 @@ object SettingsDataScreen : SearchableSettings {
         val context = LocalContext.current
         val storageDir by storageDirPref.collectAsState()
 
+        if (storageDir == storageDirPref.defaultValue()) {
+            return stringResource(MR.strings.no_location_set)
+        }
+
         return remember(storageDir) {
             val file = UniFile.fromUri(context, storageDir.toUri())
             file?.filePath ?: file?.uri?.toString()

+ 2 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt

@@ -33,6 +33,7 @@ import eu.kanade.presentation.library.LibrarySettingsDialog
 import eu.kanade.presentation.library.components.LibraryContent
 import eu.kanade.presentation.library.components.LibraryToolbar
 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.tachiyomi.R
 import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
@@ -163,7 +164,7 @@ object LibraryTab : Tab {
                             EmptyScreenAction(
                                 stringRes = MR.strings.getting_started_guide,
                                 icon = Icons.AutoMirrored.Outlined.HelpOutline,
-                                onClick = { handler.openUri("https://tachiyomi.org/docs/guides/getting-started") },
+                                onClick = { handler.openUri(GETTING_STARTED_URL) },
                             ),
                         ),
                     )

+ 10 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt

@@ -8,6 +8,7 @@ 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
@@ -22,12 +23,18 @@ class OnboardingScreen : Screen() {
         val storagePreferences = remember { Injekt.get<StoragePreferences>() }
         val uiPreferences = remember { Injekt.get<UiPreferences>() }
 
+        val finishOnboarding = {
+            basePreferences.shownOnboardingFlow().set(true)
+            navigator.pop()
+        }
+
         OnboardingScreen(
             storagePreferences = storagePreferences,
             uiPreferences = uiPreferences,
-            onComplete = {
-                basePreferences.shownOnboardingFlow().set(true)
-                navigator.pop()
+            onComplete = { finishOnboarding() },
+            onRestoreBackup = {
+                finishOnboarding()
+                navigator.push(SettingsScreen.toDataAndStorageScreen())
             },
         )
     }

+ 5 - 1
i18n/src/commonMain/resources/MR/base/strings.xml

@@ -180,7 +180,10 @@
     <string name="onboarding_action_next">Next</string>
     <string name="onboarding_action_finish">Get started</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 -->
       <!-- Subsections -->
@@ -439,6 +442,7 @@
     <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_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="disabled">Disabled</string>
     <string name="last_read_chapter">Last read chapter</string>