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
import androidx.compose.animation.Crossfade
@ -12,6 +20,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
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.sp
@ -29,12 +38,12 @@ fun Banner(
val lightOnSurface = lightThemeColors.onSurface
Surface(
modifier = modifier.fillMaxWidth(),
modifier = modifier.fillMaxWidth().height(88.dp),
color = MaterialTheme.colorScheme.surface,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.fillMaxSize()
.padding(horizontal = 16.dp)
.clip(RoundedCornerShape(32.dp))
.background(lightPrimaryContainer)
@ -52,19 +61,24 @@ fun Banner(
)
}
}
Column(modifier = Modifier.weight(1f)) {
Column(
modifier = Modifier.weight(1f).fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = title,
maxLines = if (desc == null) 2 else 1,
style = MaterialTheme.typography.titleLarge.copy(fontSize = 20.sp),
color = lightOnSurface,
overflow = TextOverflow.Ellipsis,
)
desc?.let {
Text(
text = it,
maxLines = 1,
style = MaterialTheme.typography.bodyMedium,
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
import androidx.compose.animation.*
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
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.statusBarsPadding
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.systemuicontroller.rememberSystemUiController
import me.ash.reader.ui.ext.animatedComposable
import me.ash.reader.ui.page.home.HomePage
import me.ash.reader.ui.page.settings.SettingsPage
import me.ash.reader.ui.theme.AppTheme
@ -47,86 +44,10 @@ fun HomeEntry() {
navController = navController,
startDestination = RouteName.HOME,
) {
composable(
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))
},
) {
animatedComposable(route = RouteName.HOME) {
HomePage(navController)
}
composable(
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))
},
) {
animatedComposable(route = RouteName.SETTINGS) {
SettingsPage(navController)
}
}

View File

@ -72,7 +72,7 @@ data class FilterState(
@OptIn(ExperimentalPagerApi::class)
data class HomeViewState(
val pagerState: PagerState = PagerState(1),
val pagerState: PagerState = PagerState(0),
)
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.getDesc
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.FilterState
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeDialog
@ -91,7 +92,6 @@ fun FeedsPage(
feedsViewModel.dispatch(FeedsViewAction.FetchAccount)
}
LaunchedEffect(filterState) {
feedsViewModel.dispatch(
FeedsViewAction.FetchData(filterState)
@ -111,7 +111,7 @@ fun FeedsPage(
contentDescription = stringResource(R.string.settings),
tint = MaterialTheme.colorScheme.onSurface,
) {
onScrollToPage(0)
navController.navigate(RouteName.SETTINGS)
}
},
actions = {

View File

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

View File

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

View File

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

View File

@ -53,4 +53,17 @@
<string name="one_day">1天</string>
<string name="three_days">3天</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>

View File

@ -53,4 +53,17 @@
<string name="one_day">1d</string>
<string name="three_days">3d</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>