Add SettingsPage

This commit is contained in:
Ash 2022-04-04 17:56:49 +08:00
parent efba776db3
commit 590470137a
11 changed files with 246 additions and 145 deletions

View File

@ -1,3 +1,11 @@
/**
* Copyright (C) 2021 Kyant0
*
* @link https://github.com/Kyant0/MusicYou
* @author Kyant0
* @modifier Ashinch
*/
package me.ash.reader.ui.component package me.ash.reader.ui.component
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
@ -12,6 +20,7 @@ import androidx.compose.ui.Alignment
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.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@ -29,12 +38,12 @@ fun Banner(
val lightOnSurface = lightThemeColors.onSurface val lightOnSurface = lightThemeColors.onSurface
Surface( Surface(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth().height(88.dp),
color = MaterialTheme.colorScheme.surface, color = MaterialTheme.colorScheme.surface,
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxSize()
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
.clip(RoundedCornerShape(32.dp)) .clip(RoundedCornerShape(32.dp))
.background(lightPrimaryContainer) .background(lightPrimaryContainer)
@ -52,19 +61,24 @@ fun Banner(
) )
} }
} }
Column(modifier = Modifier.weight(1f)) { Column(
modifier = Modifier.weight(1f).fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween,
) {
Text( Text(
text = title, text = title,
maxLines = if (desc == null) 2 else 1, maxLines = if (desc == null) 2 else 1,
style = MaterialTheme.typography.titleLarge.copy(fontSize = 20.sp), style = MaterialTheme.typography.titleLarge.copy(fontSize = 20.sp),
color = lightOnSurface, color = lightOnSurface,
overflow = TextOverflow.Ellipsis,
) )
desc?.let { desc?.let {
Text( Text(
text = it, text = it,
maxLines = 1,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = lightOnSurface.copy(alpha = 0.7f), color = lightOnSurface.copy(alpha = 0.7f),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
) )
} }
} }

View File

@ -0,0 +1,81 @@
/**
* Copyright (C) 2021 Kyant0
*
* @link https://github.com/Kyant0/MusicYou
* @author Kyant0
* @modifier Ashinch
*/
package me.ash.reader.ui.component
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun SelectableSettingGroupItem(
modifier: Modifier = Modifier,
selected: Boolean = false,
title: String,
desc: String? = null,
icon: ImageVector? = null,
onClick: () -> Unit,
) {
Surface(
modifier = modifier.clickable { onClick() },
color = Color.Unspecified,
contentColor = if (selected) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.onSurface
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.background(
if (selected) MaterialTheme.colorScheme.onSurface else Color.Unspecified,
RoundedCornerShape(24.dp)
)
.padding(8.dp, 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
icon?.let {
Icon(
imageVector = it,
contentDescription = null,
modifier = Modifier.padding(start = 8.dp, end = 16.dp),
tint = if (selected) MaterialTheme.colorScheme.surface.copy(alpha = 0.8f) else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f),
)
}
Column(modifier = Modifier.weight(1f)) {
Text(
text = title,
maxLines = if (desc == null) 2 else 1,
style = MaterialTheme.typography.titleLarge.copy(fontSize = 20.sp),
)
desc?.let {
Text(
text = it,
color = if (selected) MaterialTheme.colorScheme.surface.copy(alpha = 0.7f)
else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f),
maxLines = 1,
style = MaterialTheme.typography.bodyMedium,
)
}
}
}
}
}

View File

@ -0,0 +1,51 @@
/**
* Copyright (C) 2021 Kyant0
*
* @link https://github.com/Kyant0/MusicYou
* @author Kyant0
* @modifier Ashinch
*/
package me.ash.reader.ui.ext
import androidx.compose.animation.*
import androidx.compose.animation.core.tween
import androidx.compose.runtime.Composable
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavDeepLink
import androidx.navigation.NavGraphBuilder
import com.google.accompanist.navigation.animation.composable
@OptIn(ExperimentalAnimationApi::class)
fun NavGraphBuilder.animatedComposable(
route: String,
arguments: List<NamedNavArgument> = emptyList(),
deepLinks: List<NavDeepLink> = emptyList(),
content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit
) = composable(
route = route,
arguments = arguments,
deepLinks = deepLinks,
enterTransition = {
fadeIn(animationSpec = tween(220, delayMillis = 90)) +
scaleIn(
initialScale = 0.92f,
animationSpec = tween(220, delayMillis = 90)
)
},
exitTransition = {
fadeOut(animationSpec = tween(90))
},
popEnterTransition = {
fadeIn(animationSpec = tween(220, delayMillis = 90)) +
scaleIn(
initialScale = 0.92f,
animationSpec = tween(220, delayMillis = 90)
)
},
popExitTransition = {
fadeOut(animationSpec = tween(90))
},
content = content
)

View File

@ -1,9 +1,6 @@
package me.ash.reader.ui.page.common package me.ash.reader.ui.page.common
import androidx.compose.animation.* import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -18,9 +15,9 @@ import com.google.accompanist.insets.ProvideWindowInsets
import com.google.accompanist.insets.navigationBarsHeight import com.google.accompanist.insets.navigationBarsHeight
import com.google.accompanist.insets.statusBarsPadding import com.google.accompanist.insets.statusBarsPadding
import com.google.accompanist.navigation.animation.AnimatedNavHost import com.google.accompanist.navigation.animation.AnimatedNavHost
import com.google.accompanist.navigation.animation.composable
import com.google.accompanist.navigation.animation.rememberAnimatedNavController import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import me.ash.reader.ui.ext.animatedComposable
import me.ash.reader.ui.page.home.HomePage import me.ash.reader.ui.page.home.HomePage
import me.ash.reader.ui.page.settings.SettingsPage import me.ash.reader.ui.page.settings.SettingsPage
import me.ash.reader.ui.theme.AppTheme import me.ash.reader.ui.theme.AppTheme
@ -47,86 +44,10 @@ fun HomeEntry() {
navController = navController, navController = navController,
startDestination = RouteName.HOME, startDestination = RouteName.HOME,
) { ) {
composable( animatedComposable(route = RouteName.HOME) {
route = RouteName.HOME,
enterTransition = {
slideInHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
initialOffsetX = { -it }
) + fadeIn(animationSpec = tween(220, delayMillis = 90))
},
exitTransition = {
slideOutHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
targetOffsetX = { it }
) + fadeOut(animationSpec = tween(220, delayMillis = 90))
},
popEnterTransition = {
slideInHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
initialOffsetX = { -it }
) + fadeIn(animationSpec = tween(220, delayMillis = 90))
},
popExitTransition = {
slideOutHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
targetOffsetX = { it }
) + fadeOut(animationSpec = tween(220, delayMillis = 90))
},
) {
HomePage(navController) HomePage(navController)
} }
composable( animatedComposable(route = RouteName.SETTINGS) {
route = RouteName.SETTINGS,
enterTransition = {
slideInHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
initialOffsetX = { -it }
) + fadeIn(animationSpec = tween(220, delayMillis = 90))
},
exitTransition = {
slideOutHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
targetOffsetX = { -it }
) + fadeOut(animationSpec = tween(220, delayMillis = 90))
},
popEnterTransition = {
slideInHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
initialOffsetX = { -it }
) + fadeIn(animationSpec = tween(220, delayMillis = 90))
},
popExitTransition = {
slideOutHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
targetOffsetX = { -it }
) + fadeOut(animationSpec = tween(220, delayMillis = 90))
},
) {
SettingsPage(navController) SettingsPage(navController)
} }
} }

View File

@ -72,7 +72,7 @@ data class FilterState(
@OptIn(ExperimentalPagerApi::class) @OptIn(ExperimentalPagerApi::class)
data class HomeViewState( data class HomeViewState(
val pagerState: PagerState = PagerState(1), val pagerState: PagerState = PagerState(0),
) )
sealed class HomeViewAction { sealed class HomeViewAction {

View File

@ -35,6 +35,7 @@ import me.ash.reader.ui.component.Subtitle
import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.ext.getDesc import me.ash.reader.ui.ext.getDesc
import me.ash.reader.ui.ext.getName import me.ash.reader.ui.ext.getName
import me.ash.reader.ui.page.common.RouteName
import me.ash.reader.ui.page.home.FilterBar import me.ash.reader.ui.page.home.FilterBar
import me.ash.reader.ui.page.home.FilterState import me.ash.reader.ui.page.home.FilterState
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeDialog import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeDialog
@ -91,7 +92,6 @@ fun FeedsPage(
feedsViewModel.dispatch(FeedsViewAction.FetchAccount) feedsViewModel.dispatch(FeedsViewAction.FetchAccount)
} }
LaunchedEffect(filterState) { LaunchedEffect(filterState) {
feedsViewModel.dispatch( feedsViewModel.dispatch(
FeedsViewAction.FetchData(filterState) FeedsViewAction.FetchData(filterState)
@ -111,7 +111,7 @@ fun FeedsPage(
contentDescription = stringResource(R.string.settings), contentDescription = stringResource(R.string.settings),
tint = MaterialTheme.colorScheme.onSurface, tint = MaterialTheme.colorScheme.onSurface,
) { ) {
onScrollToPage(0) navController.navigate(RouteName.SETTINGS)
} }
}, },
actions = { actions = {

View File

@ -52,7 +52,7 @@ fun ResultView(
onAddNewGroup: () -> Unit = {}, onAddNewGroup: () -> Unit = {},
) { ) {
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
if (groups.isNotEmpty()) onGroupClick(groups.first().id) if (groups.isNotEmpty() && selectedGroupId.isEmpty()) onGroupClick(groups.first().id)
} }
Column( Column(

View File

@ -147,7 +147,7 @@ private fun TopBar(
FeedbackIconButton( FeedbackIconButton(
isHaptic = false, isHaptic = false,
imageVector = Icons.Rounded.Close, imageVector = Icons.Rounded.Close,
contentDescription = stringResource(R.string.back), contentDescription = stringResource(R.string.close),
tint = MaterialTheme.colorScheme.onSurface tint = MaterialTheme.colorScheme.onSurface
) { ) {
onScrollToPage(1) { onScrollToPage(1) {

View File

@ -3,10 +3,10 @@ package me.ash.reader.ui.page.settings
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.* import androidx.compose.material.icons.outlined.*
import androidx.compose.material.icons.rounded.ArrowBackIosNew import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.Close
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -19,90 +19,98 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import me.ash.reader.R import me.ash.reader.R
import me.ash.reader.ui.component.Banner
import me.ash.reader.ui.component.DisplayText
import me.ash.reader.ui.component.FeedbackIconButton
import me.ash.reader.ui.component.SelectableSettingGroupItem
import me.ash.reader.ui.ext.paddingFixedHorizontal import me.ash.reader.ui.ext.paddingFixedHorizontal
import me.ash.reader.ui.ext.roundClick import me.ash.reader.ui.ext.roundClick
import me.ash.reader.ui.page.common.RouteName
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun SettingsPage( fun SettingsPage(
navController: NavHostController, navController: NavHostController,
) { ) {
val listState = rememberLazyListState() Scaffold(
Box(modifier = Modifier.fillMaxSize()) { modifier = Modifier.background(MaterialTheme.colorScheme.surface),
// LargeTopAppBar( topBar = {
// title = { Text(text = "Settings") }
// )
Column {
SmallTopAppBar( SmallTopAppBar(
title = {}, title = {},
navigationIcon = { navigationIcon = {
IconButton(onClick = { navController.popBackStack() }) { FeedbackIconButton(
Icon( isHaptic = false,
imageVector = Icons.Rounded.ArrowBackIosNew, imageVector = Icons.Rounded.ArrowBack,
contentDescription = stringResource(R.string.back), contentDescription = stringResource(R.string.back),
tint = MaterialTheme.colorScheme.primary tint = MaterialTheme.colorScheme.onSurface
) ) {
navController.navigate(RouteName.HOME)
} }
}, },
actions = {}
) )
LazyColumn( },
modifier = Modifier content = {
.weight(1f) LazyColumn {
.paddingFixedHorizontal(),
state = listState
) {
item { item {
Spacer(modifier = Modifier.height(112.dp)) DisplayText(text = stringResource(R.string.settings), desc = "")
} }
item { item {
Item( Banner(
title = "通用", title = stringResource(R.string.get_new_updates),
description = "应用的基本设置", desc = stringResource(R.string.get_new_updates_desc),
imageVector = Icons.Outlined.Apps, icon = Icons.Outlined.Lightbulb,
action = {
Icon(
imageVector = Icons.Rounded.Close,
contentDescription = stringResource(R.string.close),
)
},
) )
Spacer(modifier = Modifier.height(16.dp))
} }
item { item {
Item( SelectableSettingGroupItem(
title = "外观", title = stringResource(R.string.accounts),
description = "字体、颜色、背景", desc = stringResource(R.string.accounts_desc),
imageVector = Icons.Outlined.ColorLens, icon = Icons.Outlined.AccountCircle,
) ) {}
} }
item { item {
Item( SelectableSettingGroupItem(
title = "阅读", title = stringResource(R.string.color_and_style),
description = "渲染阅读视图的设置", desc = stringResource(R.string.color_and_style_desc),
imageVector = Icons.Outlined.LocalLibrary, icon = Icons.Outlined.Palette,
) ) {}
} }
item { item {
Item( SelectableSettingGroupItem(
title = "Ash", title = stringResource(R.string.interaction),
description = "本地账户", desc = stringResource(R.string.interaction_desc),
imageVector = Icons.Outlined.Storage, icon = Icons.Outlined.TouchApp,
) ) {}
} }
item { item {
Item( SelectableSettingGroupItem(
title = "添加账户", title = stringResource(R.string.languages),
description = "FreshRSS、Inoreader、Feedly", desc = stringResource(R.string.languages_desc),
imageVector = Icons.Outlined.AccountCircle, icon = Icons.Outlined.Language,
) ) {}
} }
item { item {
Spacer(modifier = Modifier.height(500.dp)) SelectableSettingGroupItem(
Item( title = stringResource(R.string.tips_and_support),
title = "添加账户", desc = stringResource(R.string.tips_and_support_desc),
description = "FreshRSS、Inoreader、Feedly", icon = Icons.Outlined.TipsAndUpdates,
imageVector = Icons.Outlined.AccountCircle, ) {}
)
} }
} }
} }
} )
} }
@Composable @Composable
fun Item( fun SettingsItem(
title: String = "", title: String = "",
description: String = "", description: String = "",
imageVector: ImageVector, imageVector: ImageVector,

View File

@ -53,4 +53,17 @@
<string name="one_day">1天</string> <string name="one_day">1天</string>
<string name="three_days">3天</string> <string name="three_days">3天</string>
<string name="seven_days">7天</string> <string name="seven_days">7天</string>
<string name="close">关闭</string>
<string name="get_new_updates">获取新的更新</string>
<string name="get_new_updates_desc">版本 0.6.1 现已发布</string>
<string name="accounts">账户</string>
<string name="accounts_desc">本地、Fresh</string>
<string name="color_and_style">颜色和样式</string>
<string name="color_and_style_desc">主题、色彩系统、字体大小</string>
<string name="interaction">交互</string>
<string name="interaction_desc">布局、触觉反馈</string>
<string name="languages">语言</string>
<string name="languages_desc">英语、中文</string>
<string name="tips_and_support">提示和支持</string>
<string name="tips_and_support_desc">关于、开源</string>
</resources> </resources>

View File

@ -53,4 +53,17 @@
<string name="one_day">1d</string> <string name="one_day">1d</string>
<string name="three_days">3d</string> <string name="three_days">3d</string>
<string name="seven_days">7d</string> <string name="seven_days">7d</string>
<string name="close">Close</string>
<string name="get_new_updates">Get New updates</string>
<string name="get_new_updates_desc">Version 0.6.1 has been released</string>
<string name="accounts">Accounts</string>
<string name="accounts_desc">Local, Fresh</string>
<string name="color_and_style">Color &amp; style</string>
<string name="color_and_style_desc">Theme, color system, font size</string>
<string name="interaction">Interaction</string>
<string name="interaction_desc">Layout, haptic feedback</string>
<string name="languages">Languages</string>
<string name="languages_desc">English, Chinese</string>
<string name="tips_and_support">Tips &amp; support</string>
<string name="tips_and_support_desc">About, open source</string>
</resources> </resources>