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

Unify layout for new update and crash screens

arkon 2 жил өмнө
parent
commit
01ec26842d

+ 141 - 0
app/src/main/java/eu/kanade/presentation/components/InfoScaffold.kt

@@ -0,0 +1,141 @@
+package eu.kanade.presentation.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.windowInsetsPadding
+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.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.NavigationBarDefaults
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.zIndex
+import eu.kanade.presentation.theme.TachiyomiTheme
+import eu.kanade.presentation.util.ThemePreviews
+import eu.kanade.presentation.util.padding
+import eu.kanade.presentation.util.secondaryItemAlpha
+
+@Composable
+fun InfoScaffold(
+    icon: ImageVector,
+    headingText: String,
+    subtitleText: String,
+    acceptText: String,
+    onAcceptClick: () -> Unit,
+    rejectText: String,
+    onRejectClick: () -> Unit,
+    content: @Composable ColumnScope.() -> Unit,
+) {
+    Scaffold(
+        bottomBar = {
+            val strokeWidth = Dp.Hairline
+            val borderColor = MaterialTheme.colorScheme.outline
+            Column(
+                modifier = Modifier
+                    .background(MaterialTheme.colorScheme.background)
+                    .drawBehind {
+                        drawLine(
+                            borderColor,
+                            Offset(0f, 0f),
+                            Offset(size.width, 0f),
+                            strokeWidth.value,
+                        )
+                    }
+                    .windowInsetsPadding(NavigationBarDefaults.windowInsets)
+                    .padding(
+                        horizontal = MaterialTheme.padding.medium,
+                        vertical = MaterialTheme.padding.small,
+                    ),
+            ) {
+                androidx.compose.material3.Button(
+                    modifier = Modifier.fillMaxWidth(),
+                    onClick = onAcceptClick,
+                ) {
+                    Text(text = acceptText)
+                }
+                OutlinedButton(
+                    modifier = Modifier.fillMaxWidth(),
+                    onClick = onRejectClick,
+                ) {
+                    Text(text = rejectText)
+                }
+            }
+        },
+    ) { paddingValues ->
+        // Status bar scrim
+        Box(
+            modifier = Modifier
+                .zIndex(2f)
+                .secondaryItemAlpha()
+                .background(MaterialTheme.colorScheme.background)
+                .fillMaxWidth()
+                .height(paddingValues.calculateTopPadding()),
+        )
+
+        Column(
+            modifier = Modifier
+                .verticalScroll(rememberScrollState())
+                .fillMaxWidth()
+                .padding(paddingValues)
+                .padding(top = 48.dp)
+                .padding(horizontal = MaterialTheme.padding.medium),
+        ) {
+            Icon(
+                imageVector = icon,
+                contentDescription = null,
+                modifier = Modifier
+                    .padding(bottom = MaterialTheme.padding.small)
+                    .size(48.dp),
+                tint = MaterialTheme.colorScheme.primary,
+            )
+            Text(
+                text = headingText,
+                style = MaterialTheme.typography.headlineLarge,
+            )
+            Text(
+                text = subtitleText,
+                modifier = Modifier
+                    .secondaryItemAlpha()
+                    .padding(vertical = MaterialTheme.padding.small),
+                style = MaterialTheme.typography.titleSmall,
+            )
+
+            content()
+        }
+    }
+}
+
+@ThemePreviews
+@Composable
+private fun InfoScaffoldPreview() {
+    TachiyomiTheme {
+        InfoScaffold(
+            icon = Icons.Outlined.Newspaper,
+            headingText = "Heading",
+            subtitleText = "Subtitle",
+            acceptText = "Accept",
+            onAcceptClick = {},
+            rejectText = "Reject",
+            onRejectClick = {},
+        ) {
+            Text("Hello world")
+        }
+    }
+}

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

@@ -1,37 +1,22 @@
 package eu.kanade.presentation.crash
 
 import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.asPaddingValues
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.navigationBars
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.BugReport
-import androidx.compose.material3.Button
-import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedButton
-import androidx.compose.material3.Scaffold
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
-import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
+import eu.kanade.presentation.components.InfoScaffold
+import eu.kanade.presentation.theme.TachiyomiTheme
+import eu.kanade.presentation.util.ThemePreviews
 import eu.kanade.presentation.util.padding
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.util.CrashLogUtil
@@ -44,82 +29,41 @@ fun CrashScreen(
 ) {
     val scope = rememberCoroutineScope()
     val context = LocalContext.current
-    Scaffold(
-        contentWindowInsets = WindowInsets.systemBars,
-        bottomBar = {
-            val strokeWidth = Dp.Hairline
-            val borderColor = MaterialTheme.colorScheme.outline
-            Column(
-                modifier = Modifier
-                    .background(MaterialTheme.colorScheme.surface)
-                    .drawBehind {
-                        drawLine(
-                            borderColor,
-                            Offset(0f, 0f),
-                            Offset(size.width, 0f),
-                            strokeWidth.value,
-                        )
-                    }
-                    .padding(WindowInsets.navigationBars.asPaddingValues())
-                    .padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small),
-                verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
-            ) {
-                Button(
-                    onClick = {
-                        scope.launch {
-                            CrashLogUtil(context).dumpLogs()
-                        }
-                    },
-                    modifier = Modifier.fillMaxWidth(),
-                ) {
-                    Text(text = stringResource(R.string.pref_dump_crash_logs))
-                }
-                OutlinedButton(
-                    onClick = onRestartClick,
-                    modifier = Modifier.fillMaxWidth(),
-                ) {
-                    Text(text = stringResource(R.string.crash_screen_restart_application))
-                }
+
+    InfoScaffold(
+        icon = Icons.Outlined.BugReport,
+        headingText = stringResource(R.string.crash_screen_title),
+        subtitleText = stringResource(R.string.crash_screen_description, stringResource(R.string.app_name)),
+        acceptText = stringResource(R.string.pref_dump_crash_logs),
+        onAcceptClick = {
+            scope.launch {
+                CrashLogUtil(context).dumpLogs()
             }
         },
-    ) { paddingValues ->
-        Column(
+        rejectText = stringResource(R.string.crash_screen_restart_application),
+        onRejectClick = onRestartClick,
+    ) {
+        Box(
             modifier = Modifier
-                .verticalScroll(rememberScrollState())
-                .padding(paddingValues)
-                .padding(top = 56.dp)
-                .padding(horizontal = MaterialTheme.padding.medium),
-            horizontalAlignment = Alignment.CenterHorizontally,
+                .padding(vertical = MaterialTheme.padding.small)
+                .clip(MaterialTheme.shapes.small)
+                .fillMaxWidth()
+                .background(MaterialTheme.colorScheme.surfaceVariant),
         ) {
-            Icon(
-                imageVector = Icons.Outlined.BugReport,
-                contentDescription = null,
-                modifier = Modifier
-                    .size(64.dp),
-            )
-            Text(
-                text = stringResource(R.string.crash_screen_title),
-                style = MaterialTheme.typography.titleLarge,
-            )
             Text(
-                text = stringResource(R.string.crash_screen_description, stringResource(R.string.app_name)),
+                text = exception.toString(),
                 modifier = Modifier
-                    .padding(vertical = MaterialTheme.padding.small),
+                    .padding(all = MaterialTheme.padding.small),
+                color = MaterialTheme.colorScheme.onSurfaceVariant,
             )
-            Box(
-                modifier = Modifier
-                    .padding(vertical = MaterialTheme.padding.small)
-                    .clip(MaterialTheme.shapes.small)
-                    .fillMaxWidth()
-                    .background(MaterialTheme.colorScheme.surfaceVariant),
-            ) {
-                Text(
-                    text = exception.toString(),
-                    modifier = Modifier
-                        .padding(all = MaterialTheme.padding.small),
-                    color = MaterialTheme.colorScheme.onSurfaceVariant,
-                )
-            }
         }
     }
 }
+
+@ThemePreviews
+@Composable
+private fun CrashScreenPreview() {
+    TachiyomiTheme {
+        CrashScreen(exception = RuntimeException("Dummy")) {}
+    }
+}

+ 2 - 0
app/src/main/java/eu/kanade/presentation/manga/TrackInfoDialogHome.kt

@@ -39,6 +39,7 @@ import androidx.compose.ui.draw.alpha
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
 import eu.kanade.presentation.components.Divider
@@ -250,6 +251,7 @@ private fun TrackDetailsItem(
             maxLines = 2,
             overflow = TextOverflow.Ellipsis,
             style = MaterialTheme.typography.bodyMedium,
+            textAlign = TextAlign.Center,
         )
     }
 }

+ 47 - 102
app/src/main/java/eu/kanade/presentation/more/NewUpdateScreen.kt

@@ -1,41 +1,28 @@
 package eu.kanade.presentation.more
 
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.windowInsetsPadding
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.OpenInNew
 import androidx.compose.material.icons.outlined.NewReleases
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.NavigationBarDefaults
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.SpanStyle
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.zIndex
 import com.halilibo.richtext.markdown.Markdown
 import com.halilibo.richtext.ui.RichTextStyle
 import com.halilibo.richtext.ui.material3.Material3RichText
 import com.halilibo.richtext.ui.string.RichTextStringStyle
-import eu.kanade.presentation.components.Scaffold
+import eu.kanade.presentation.components.InfoScaffold
+import eu.kanade.presentation.theme.TachiyomiTheme
+import eu.kanade.presentation.util.ThemePreviews
 import eu.kanade.presentation.util.padding
-import eu.kanade.presentation.util.secondaryItemAlpha
 import eu.kanade.tachiyomi.R
 
 @Composable
@@ -46,98 +33,56 @@ fun NewUpdateScreen(
     onRejectUpdate: () -> Unit,
     onAcceptUpdate: () -> Unit,
 ) {
-    Scaffold(
-        bottomBar = {
-            val strokeWidth = Dp.Hairline
-            val borderColor = MaterialTheme.colorScheme.outline
-            Column(
-                modifier = Modifier
-                    .background(MaterialTheme.colorScheme.background)
-                    .drawBehind {
-                        drawLine(
-                            borderColor,
-                            Offset(0f, 0f),
-                            Offset(size.width, 0f),
-                            strokeWidth.value,
-                        )
-                    }
-                    .windowInsetsPadding(NavigationBarDefaults.windowInsets)
-                    .padding(
-                        horizontal = MaterialTheme.padding.medium,
-                        vertical = MaterialTheme.padding.small,
-                    ),
-            ) {
-                TextButton(
-                    modifier = Modifier.fillMaxWidth(),
-                    onClick = onAcceptUpdate,
-                ) {
-                    Text(text = stringResource(id = R.string.update_check_confirm))
-                }
-                TextButton(
-                    modifier = Modifier.fillMaxWidth(),
-                    onClick = onRejectUpdate,
-                ) {
-                    Text(text = stringResource(R.string.action_not_now))
-                }
-            }
-        },
-    ) { paddingValues ->
-        // Status bar scrim
-        Box(
+    InfoScaffold(
+        icon = Icons.Outlined.NewReleases,
+        headingText = stringResource(R.string.update_check_notification_update_available),
+        subtitleText = versionName,
+        acceptText = stringResource(id = R.string.update_check_confirm),
+        onAcceptClick = onAcceptUpdate,
+        rejectText = stringResource(R.string.action_not_now),
+        onRejectClick = onRejectUpdate,
+    ) {
+        Material3RichText(
             modifier = Modifier
-                .zIndex(2f)
-                .secondaryItemAlpha()
-                .background(MaterialTheme.colorScheme.background)
                 .fillMaxWidth()
-                .height(paddingValues.calculateTopPadding()),
-        )
-
-        Column(
-            modifier = Modifier
-                .verticalScroll(rememberScrollState())
-                .padding(paddingValues)
-                .padding(top = 48.dp)
-                .padding(horizontal = MaterialTheme.padding.medium),
+                .padding(vertical = MaterialTheme.padding.large),
+            style = RichTextStyle(
+                stringStyle = RichTextStringStyle(
+                    linkStyle = SpanStyle(color = MaterialTheme.colorScheme.primary),
+                ),
+            ),
         ) {
-            Icon(
-                imageVector = Icons.Outlined.NewReleases,
-                contentDescription = null,
-                modifier = Modifier
-                    .padding(bottom = MaterialTheme.padding.small)
-                    .size(48.dp),
-                tint = MaterialTheme.colorScheme.primary,
-            )
-            Text(
-                text = stringResource(R.string.update_check_notification_update_available),
-                style = MaterialTheme.typography.headlineLarge,
-            )
-            Text(
-                text = versionName,
-                modifier = Modifier.secondaryItemAlpha(),
-                style = MaterialTheme.typography.titleSmall,
-            )
+            Markdown(content = changelogInfo)
 
-            Material3RichText(
-                modifier = Modifier
-                    .fillMaxWidth()
-                    .padding(vertical = MaterialTheme.padding.large),
-                style = RichTextStyle(
-                    stringStyle = RichTextStringStyle(
-                        linkStyle = SpanStyle(color = MaterialTheme.colorScheme.primary),
-                    ),
-                ),
+            TextButton(
+                onClick = onOpenInBrowser,
+                modifier = Modifier.padding(top = MaterialTheme.padding.small),
             ) {
-                Markdown(content = changelogInfo)
-
-                TextButton(
-                    onClick = onOpenInBrowser,
-                    modifier = Modifier.padding(top = MaterialTheme.padding.small),
-                ) {
-                    Text(text = stringResource(R.string.update_check_open))
-                    Spacer(modifier = Modifier.width(MaterialTheme.padding.tiny))
-                    Icon(imageVector = Icons.Default.OpenInNew, contentDescription = null)
-                }
+                Text(text = stringResource(R.string.update_check_open))
+                Spacer(modifier = Modifier.width(MaterialTheme.padding.tiny))
+                Icon(imageVector = Icons.Default.OpenInNew, contentDescription = null)
             }
         }
     }
 }
+
+@ThemePreviews
+@Composable
+private fun NewUpdateScreenPreview() {
+    TachiyomiTheme {
+        NewUpdateScreen(
+            versionName = "v0.99.9",
+            changelogInfo = """
+                ## Yay
+                Foobar
+                
+                ### More info
+                - Hello
+                - World
+            """.trimIndent(),
+            onOpenInBrowser = {},
+            onRejectUpdate = {},
+            onAcceptUpdate = {},
+        )
+    }
+}

+ 7 - 7
app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt

@@ -377,14 +377,13 @@ class Downloader(
         }
 
         val digitCount = (download.pages?.size ?: 0).toString().length.coerceAtLeast(3)
-
         val filename = String.format("%0${digitCount}d", page.number)
         val tmpFile = tmpDir.findFile("$filename.tmp")
 
-        // Delete temp file if it exists.
+        // Delete temp file if it exists
         tmpFile?.delete()
 
-        // Try to find the image file.
+        // Try to find the image file
         val imageFile = tmpDir.listFiles()?.firstOrNull { it.name!!.startsWith("$filename.") || it.name!!.startsWith("${filename}__001") }
 
         // If the image is already downloaded, do nothing. Otherwise download from network
@@ -492,7 +491,7 @@ class Downloader(
         val imageFile = tmpDir.listFiles()?.firstOrNull { it.name.orEmpty().startsWith(filenamePrefix) }
             ?: throw Error(context.getString(R.string.download_notifier_split_page_not_found, page.number))
 
-        // Check if the original page was previously splitted before then skip.
+        // If the original page was previously split, then skip
         if (imageFile.name.orEmpty().startsWith("${filenamePrefix}__")) return true
 
         return try {
@@ -521,7 +520,7 @@ class Downloader(
         val downloadPageCount = download.pages?.size ?: return
         // Ensure that all pages has been downloaded
         if (download.downloadedImages < downloadPageCount) return
-        // Ensure that the chapter folder has all the pages.
+        // Ensure that the chapter folder has all the pages
         val downloadedImagesCount = tmpDir.listFiles().orEmpty().count {
             val fileName = it.name.orEmpty()
             when {
@@ -542,7 +541,8 @@ class Downloader(
 //                download.chapter.toDomainChapter()!!,
 //                chapterUrl,
 //            )
-            // Only rename the directory if it's downloaded.
+
+            // Only rename the directory if it's downloaded
             if (downloadPreferences.saveChaptersAsCBZ().get()) {
                 archiveChapter(mangaDir, dirname, tmpDir)
             } else {
@@ -621,7 +621,7 @@ class Downloader(
     private fun completeDownload(download: Download) {
         // Delete successful downloads from queue
         if (download.status == Download.State.DOWNLOADED) {
-            // remove downloaded chapter from queue
+            // Remove downloaded chapter from queue
             queue.remove(download)
         }
         if (areAllDownloadsFinished()) {

+ 2 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreen.kt

@@ -16,6 +16,7 @@ class NewUpdateScreen(
     private val releaseLink: String,
     private val downloadLink: String,
 ) : Screen {
+
     @Composable
     override fun Content() {
         val navigator = LocalNavigator.currentOrThrow
@@ -23,6 +24,7 @@ class NewUpdateScreen(
         val changelogInfoNoChecksum = remember {
             changelogInfo.replace("""---(\R|.)*Checksums(\R|.)*""".toRegex(), "")
         }
+
         NewUpdateScreen(
             versionName = versionName,
             changelogInfo = changelogInfoNoChecksum,

+ 1 - 1
i18n/src/main/res/values/strings.xml

@@ -782,7 +782,7 @@
     <string name="not_installed">Not installed</string>
 
     <!-- Crash screen -->
-    <string name="crash_screen_title">An Unexpected Error Occurred</string>
+    <string name="crash_screen_title">Whoops!</string>
     <string name="crash_screen_description">%s ran into an unexpected error. We suggest you screenshot this message, dump the crash logs, and then share it in our support channel on Discord.</string>
     <string name="crash_screen_restart_application">Restart the application</string>