pref: 首页未登录 & 空状态优化

This commit is contained in:
HuanCheng65 2023-03-11 01:18:19 +08:00
parent 3a897cd4d1
commit afe5d19e44
No known key found for this signature in database
GPG Key ID: E9031EF91A805148
4 changed files with 190 additions and 69 deletions

View File

@ -107,6 +107,9 @@ fun MainPage(
} }
} }
val pagerState = rememberPagerState()
val coroutineScope = rememberCoroutineScope()
val themeColors = ExtendedTheme.colors
val windowSizeClass = LocalWindowSizeClass.current val windowSizeClass = LocalWindowSizeClass.current
val foldingDevicePosture by devicePostureFlow.collectAsState() val foldingDevicePosture by devicePostureFlow.collectAsState()
val navigationItems = listOfNotNull( val navigationItems = listOfNotNull(
@ -114,7 +117,14 @@ fun MainPage(
icon = { if (it) Icons.Rounded.Inventory2 else Icons.Outlined.Inventory2 }, icon = { if (it) Icons.Rounded.Inventory2 else Icons.Outlined.Inventory2 },
title = stringResource(id = R.string.title_main), title = stringResource(id = R.string.title_main),
content = { content = {
HomePage() HomePage(
canOpenExplore = !LocalContext.current.appPreferences.hideExplore,
onOpenExplore = {
coroutineScope.launch {
pagerState.scrollToPage(1)
}
}
)
} }
), ),
if (LocalContext.current.appPreferences.hideExplore) null if (LocalContext.current.appPreferences.hideExplore) null
@ -149,9 +159,6 @@ fun MainPage(
} }
), ),
) )
val pagerState = rememberPagerState()
val coroutineScope = rememberCoroutineScope()
val themeColors = ExtendedTheme.colors
val navigationType = when (windowSizeClass.widthSizeClass) { val navigationType = when (windowSizeClass.widthSizeClass) {
WindowWidthSizeClass.Compact -> { WindowWidthSizeClass.Compact -> {

View File

@ -6,11 +6,13 @@ import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
@ -26,6 +28,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.DropdownMenuItem import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold import androidx.compose.material.Scaffold
import androidx.compose.material.Surface import androidx.compose.material.Surface
import androidx.compose.material.Text import androidx.compose.material.Text
@ -54,12 +57,18 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.LottieConstants
import com.airbnb.lottie.compose.rememberLottieComposition
import com.google.accompanist.drawablepainter.rememberDrawablePainter import com.google.accompanist.drawablepainter.rememberDrawablePainter
import com.google.accompanist.placeholder.placeholder import com.google.accompanist.placeholder.placeholder
import com.huanchengfly.tieba.post.R import com.huanchengfly.tieba.post.R
import com.huanchengfly.tieba.post.activities.LoginActivity
import com.huanchengfly.tieba.post.activities.NewSearchActivity import com.huanchengfly.tieba.post.activities.NewSearchActivity
import com.huanchengfly.tieba.post.arch.collectPartialAsState import com.huanchengfly.tieba.post.arch.collectPartialAsState
import com.huanchengfly.tieba.post.arch.pageViewModel import com.huanchengfly.tieba.post.arch.pageViewModel
@ -70,12 +79,14 @@ import com.huanchengfly.tieba.post.ui.page.destinations.ForumPageDestination
import com.huanchengfly.tieba.post.ui.widgets.Chip import com.huanchengfly.tieba.post.ui.widgets.Chip
import com.huanchengfly.tieba.post.ui.widgets.compose.ActionItem import com.huanchengfly.tieba.post.ui.widgets.compose.ActionItem
import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar
import com.huanchengfly.tieba.post.ui.widgets.compose.Button
import com.huanchengfly.tieba.post.ui.widgets.compose.ConfirmDialog import com.huanchengfly.tieba.post.ui.widgets.compose.ConfirmDialog
import com.huanchengfly.tieba.post.ui.widgets.compose.LongClickMenu import com.huanchengfly.tieba.post.ui.widgets.compose.LongClickMenu
import com.huanchengfly.tieba.post.ui.widgets.compose.Toolbar import com.huanchengfly.tieba.post.ui.widgets.compose.Toolbar
import com.huanchengfly.tieba.post.ui.widgets.compose.accountNavIconIfCompact import com.huanchengfly.tieba.post.ui.widgets.compose.accountNavIconIfCompact
import com.huanchengfly.tieba.post.ui.widgets.compose.rememberDialogState import com.huanchengfly.tieba.post.ui.widgets.compose.rememberDialogState
import com.huanchengfly.tieba.post.ui.widgets.compose.rememberMenuState import com.huanchengfly.tieba.post.ui.widgets.compose.rememberMenuState
import com.huanchengfly.tieba.post.ui.widgets.compose.states.StateScreen
import com.huanchengfly.tieba.post.utils.AccountUtil.LocalAccount import com.huanchengfly.tieba.post.utils.AccountUtil.LocalAccount
import com.huanchengfly.tieba.post.utils.ImageUtil import com.huanchengfly.tieba.post.utils.ImageUtil
import com.huanchengfly.tieba.post.utils.TiebaUtil import com.huanchengfly.tieba.post.utils.TiebaUtil
@ -362,8 +373,10 @@ fun HomePage(
HomeUiIntent.Refresh HomeUiIntent.Refresh
) )
), ),
canOpenExplore: Boolean = false,
onOpenExplore: () -> Unit = {},
) { ) {
LocalAccount.current val account = LocalAccount.current
val context = LocalContext.current val context = LocalContext.current
val isLoading by viewModel.uiState.collectPartialAsState( val isLoading by viewModel.uiState.collectPartialAsState(
prop1 = HomeUiState::isLoading, prop1 = HomeUiState::isLoading,
@ -379,6 +392,7 @@ fun HomePage(
) )
var listSingle by remember { mutableStateOf(context.appPreferences.listSingle) } var listSingle by remember { mutableStateOf(context.appPreferences.listSingle) }
val gridCells by remember { derivedStateOf { getGridCells(context, listSingle) } } val gridCells by remember { derivedStateOf { getGridCells(context, listSingle) } }
Scaffold( Scaffold(
backgroundColor = Color.Transparent, backgroundColor = Color.Transparent,
topBar = { topBar = {
@ -404,26 +418,42 @@ fun HomePage(
}, },
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
) { contentPaddings -> ) { contentPaddings ->
val pullRefreshState = rememberPullRefreshState( StateScreen(
refreshing = isLoading, isEmpty = forums.isEmpty(),
onRefresh = { viewModel.send(HomeUiIntent.Refresh) }) isError = false,
Box(modifier = Modifier isLoading = isLoading,
.padding(contentPaddings) modifier = Modifier.padding(contentPaddings),
.pullRefresh(pullRefreshState)) { emptyScreen = {
val gridState = rememberLazyGridState() EmptyScreen(
LazyVerticalGrid( loggedIn = account != null,
state = gridState, canOpenExplore = canOpenExplore,
columns = gridCells, onOpenExplore = onOpenExplore
contentPadding = PaddingValues(bottom = 12.dp), )
},
loadingScreen = {
HomePageSkeletonScreen(listSingle = listSingle, gridCells = gridCells)
}
) {
val pullRefreshState = rememberPullRefreshState(
refreshing = isLoading,
onRefresh = { viewModel.send(HomeUiIntent.Refresh) })
Box(
modifier = Modifier modifier = Modifier
.fillMaxSize(), .pullRefresh(pullRefreshState)
) { ) {
item(key = "SearchBox", span = { GridItemSpan(maxLineSpan) }) { val gridState = rememberLazyGridState()
SearchBox(modifier = Modifier.padding(bottom = 12.dp)) { LazyVerticalGrid(
context.goToActivity<NewSearchActivity>() state = gridState,
columns = gridCells,
contentPadding = PaddingValues(bottom = 12.dp),
modifier = Modifier
.fillMaxSize(),
) {
item(key = "SearchBox", span = { GridItemSpan(maxLineSpan) }) {
SearchBox(modifier = Modifier.padding(bottom = 12.dp)) {
context.goToActivity<NewSearchActivity>()
}
} }
}
if (!isLoading || forums.isNotEmpty()) {
if (topForums.isNotEmpty()) { if (topForums.isNotEmpty()) {
item(key = "TopForumHeader", span = { GridItemSpan(maxLineSpan) }) { item(key = "TopForumHeader", span = { GridItemSpan(maxLineSpan) }) {
Column { Column {
@ -458,53 +488,126 @@ fun HomePage(
val item = forums[it] val item = forums[it]
ForumItem(viewModel, item, listSingle) ForumItem(viewModel, item, listSingle)
} }
} else {
item(key = "TopForumHeaderPlaceholder", span = { GridItemSpan(maxLineSpan) }) {
Column {
Header(
text = stringResource(id = R.string.title_top_forum),
invert = true,
modifier = Modifier.placeholder(visible = true, color = ExtendedTheme.colors.chip)
)
Spacer(modifier = Modifier.height(8.dp))
}
}
items(6, key = { "TopPlaceholder$it" }) {
ForumItemPlaceholder(listSingle)
}
item(
key = "Spacer",
span = { GridItemSpan(maxLineSpan) }) {
Spacer(
modifier = Modifier.height(
16.dp
)
)
}
item(key = "ForumHeaderPlaceholder", span = { GridItemSpan(maxLineSpan) }) {
Column {
Header(
text = stringResource(id = R.string.forum_list_title),
invert = true,
modifier = Modifier.placeholder(
visible = true,
color = ExtendedTheme.colors.chip
)
)
Spacer(modifier = Modifier.height(8.dp))
}
}
items(12, key = { "Placeholder$it" }) {
ForumItemPlaceholder(listSingle)
}
} }
}
PullRefreshIndicator( PullRefreshIndicator(
refreshing = isLoading, refreshing = isLoading,
state = pullRefreshState, state = pullRefreshState,
modifier = Modifier.align(Alignment.TopCenter) modifier = Modifier.align(Alignment.TopCenter)
)
}
}
}
}
@Composable
private fun HomePageSkeletonScreen(
listSingle: Boolean,
gridCells: GridCells
) {
LazyVerticalGrid(
columns = gridCells,
contentPadding = PaddingValues(bottom = 12.dp),
modifier = Modifier
.fillMaxSize(),
) {
item(key = "TopForumHeaderPlaceholder", span = { GridItemSpan(maxLineSpan) }) {
Column {
Header(
text = stringResource(id = R.string.title_top_forum),
invert = true,
modifier = Modifier.placeholder(
visible = true,
color = ExtendedTheme.colors.chip
)
)
Spacer(modifier = Modifier.height(8.dp))
}
}
items(6, key = { "TopPlaceholder$it" }) {
ForumItemPlaceholder(listSingle)
}
item(
key = "Spacer",
span = { GridItemSpan(maxLineSpan) }) {
Spacer(
modifier = Modifier.height(
16.dp
)
) )
} }
item(key = "ForumHeaderPlaceholder", span = { GridItemSpan(maxLineSpan) }) {
Column {
Header(
text = stringResource(id = R.string.forum_list_title),
invert = true,
modifier = Modifier.placeholder(
visible = true,
color = ExtendedTheme.colors.chip
)
)
Spacer(modifier = Modifier.height(8.dp))
}
}
items(12, key = { "Placeholder$it" }) {
ForumItemPlaceholder(listSingle)
}
}
}
@Composable
fun EmptyScreen(
loggedIn: Boolean,
canOpenExplore: Boolean,
onOpenExplore: () -> Unit
) {
val context = LocalContext.current
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp, alignment = CenterVertically)
) {
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.lottie_astronaut))
LottieAnimation(
composition = composition,
iterations = LottieConstants.IterateForever,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(2f)
)
Text(
text = stringResource(id = R.string.title_empty),
style = MaterialTheme.typography.h6,
color = ExtendedTheme.colors.text,
textAlign = TextAlign.Center,
)
if (!loggedIn) {
Text(
text = stringResource(id = R.string.title_empty_login),
style = MaterialTheme.typography.body1,
color = ExtendedTheme.colors.textSecondary,
textAlign = TextAlign.Center
)
Button(
onClick = {
context.goToActivity<LoginActivity>()
},
modifier = Modifier
.fillMaxWidth()
) {
Text(text = stringResource(id = R.string.button_login))
}
}
if (canOpenExplore) {
Button(
onClick = onOpenExplore,
modifier = Modifier
.fillMaxWidth()
) {
Text(text = stringResource(id = R.string.button_go_to_explore))
}
}
} }
} }

View File

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ButtonColors import androidx.compose.material.ButtonColors
import androidx.compose.material.ButtonDefaults import androidx.compose.material.ButtonDefaults
import androidx.compose.material.ButtonElevation import androidx.compose.material.ButtonElevation
@ -23,6 +24,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
@ -31,10 +33,13 @@ fun Button(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
elevation: ButtonElevation? = ButtonDefaults.elevation(), elevation: ButtonElevation? = ButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp, 0.dp),
shape: Shape = MaterialTheme.shapes.small, shape: Shape = RoundedCornerShape(100),
border: BorderStroke? = null, border: BorderStroke? = null,
colors: ButtonColors = ButtonDefaults.buttonColors(), colors: ButtonColors = ButtonDefaults.buttonColors(
backgroundColor = ExtendedTheme.colors.accent,
contentColor = ExtendedTheme.colors.onAccent
),
contentPadding: PaddingValues = ButtonDefaults.ContentPadding, contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: @Composable RowScope.() -> Unit content: @Composable RowScope.() -> Unit
) { ) {

View File

@ -642,4 +642,10 @@
<string name="message_dialog_oksign_battery_optimization">在 Android 12 及以上版本中,后台启动前台服务被严格限制。为了避免一键签到无法正常启动,请忽略本应用的“电池优化”。</string> <string name="message_dialog_oksign_battery_optimization">在 Android 12 及以上版本中,后台启动前台服务被严格限制。为了避免一键签到无法正常启动,请忽略本应用的“电池优化”。</string>
<string name="button_go_to_ignore_battery_optimization">好的,去忽略</string> <string name="button_go_to_ignore_battery_optimization">好的,去忽略</string>
<string name="button_dont_remind_again">不再提示</string> <string name="button_dont_remind_again">不再提示</string>
<string name="link">链接</string>
<string name="user">用户</string>
<string name="title_empty">这里什么都没有</string>
<string name="title_empty_login">请登录之后查看你的关注。\n或者你也可以先去逛逛发现</string>
<string name="button_login">去登录</string>
<string name="button_go_to_explore">随便看看</string>
</resources> </resources>