diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/MainPage.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/MainPage.kt index d2609d96..680b5d73 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/MainPage.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/MainPage.kt @@ -107,6 +107,9 @@ fun MainPage( } } + val pagerState = rememberPagerState() + val coroutineScope = rememberCoroutineScope() + val themeColors = ExtendedTheme.colors val windowSizeClass = LocalWindowSizeClass.current val foldingDevicePosture by devicePostureFlow.collectAsState() val navigationItems = listOfNotNull( @@ -114,7 +117,14 @@ fun MainPage( icon = { if (it) Icons.Rounded.Inventory2 else Icons.Outlined.Inventory2 }, title = stringResource(id = R.string.title_main), content = { - HomePage() + HomePage( + canOpenExplore = !LocalContext.current.appPreferences.hideExplore, + onOpenExplore = { + coroutineScope.launch { + pagerState.scrollToPage(1) + } + } + ) } ), 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) { WindowWidthSizeClass.Compact -> { diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/home/HomePage.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/home/HomePage.kt index 5965a7ea..16f0767f 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/home/HomePage.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/home/HomePage.kt @@ -6,11 +6,13 @@ import androidx.compose.animation.animateContentSize import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth 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.ExperimentalMaterialApi import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold import androidx.compose.material.Surface 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.vectorResource 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.unit.dp 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.placeholder.placeholder 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.arch.collectPartialAsState 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.compose.ActionItem 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.LongClickMenu 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.rememberDialogState 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.ImageUtil import com.huanchengfly.tieba.post.utils.TiebaUtil @@ -362,8 +373,10 @@ fun HomePage( HomeUiIntent.Refresh ) ), + canOpenExplore: Boolean = false, + onOpenExplore: () -> Unit = {}, ) { - LocalAccount.current + val account = LocalAccount.current val context = LocalContext.current val isLoading by viewModel.uiState.collectPartialAsState( prop1 = HomeUiState::isLoading, @@ -379,6 +392,7 @@ fun HomePage( ) var listSingle by remember { mutableStateOf(context.appPreferences.listSingle) } val gridCells by remember { derivedStateOf { getGridCells(context, listSingle) } } + Scaffold( backgroundColor = Color.Transparent, topBar = { @@ -404,26 +418,42 @@ fun HomePage( }, modifier = Modifier.fillMaxSize(), ) { contentPaddings -> - val pullRefreshState = rememberPullRefreshState( - refreshing = isLoading, - onRefresh = { viewModel.send(HomeUiIntent.Refresh) }) - Box(modifier = Modifier - .padding(contentPaddings) - .pullRefresh(pullRefreshState)) { - val gridState = rememberLazyGridState() - LazyVerticalGrid( - state = gridState, - columns = gridCells, - contentPadding = PaddingValues(bottom = 12.dp), + StateScreen( + isEmpty = forums.isEmpty(), + isError = false, + isLoading = isLoading, + modifier = Modifier.padding(contentPaddings), + emptyScreen = { + EmptyScreen( + loggedIn = account != null, + canOpenExplore = canOpenExplore, + onOpenExplore = onOpenExplore + ) + }, + loadingScreen = { + HomePageSkeletonScreen(listSingle = listSingle, gridCells = gridCells) + } + ) { + val pullRefreshState = rememberPullRefreshState( + refreshing = isLoading, + onRefresh = { viewModel.send(HomeUiIntent.Refresh) }) + Box( modifier = Modifier - .fillMaxSize(), + .pullRefresh(pullRefreshState) ) { - item(key = "SearchBox", span = { GridItemSpan(maxLineSpan) }) { - SearchBox(modifier = Modifier.padding(bottom = 12.dp)) { - context.goToActivity() + val gridState = rememberLazyGridState() + LazyVerticalGrid( + 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() + } } - } - if (!isLoading || forums.isNotEmpty()) { if (topForums.isNotEmpty()) { item(key = "TopForumHeader", span = { GridItemSpan(maxLineSpan) }) { Column { @@ -458,53 +488,126 @@ fun HomePage( val item = forums[it] 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( - refreshing = isLoading, - state = pullRefreshState, - modifier = Modifier.align(Alignment.TopCenter) + PullRefreshIndicator( + refreshing = isLoading, + state = pullRefreshState, + 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() + }, + 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)) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/widgets/compose/Buttons.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/widgets/compose/Buttons.kt index 06065185..133f5f1a 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/widgets/compose/Buttons.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/widgets/compose/Buttons.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ButtonColors import androidx.compose.material.ButtonDefaults import androidx.compose.material.ButtonElevation @@ -23,6 +24,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.dp +import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme @OptIn(ExperimentalMaterialApi::class) @Composable @@ -31,10 +33,13 @@ fun Button( modifier: Modifier = Modifier, enabled: Boolean = true, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - elevation: ButtonElevation? = ButtonDefaults.elevation(), - shape: Shape = MaterialTheme.shapes.small, + elevation: ButtonElevation? = ButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp, 0.dp), + shape: Shape = RoundedCornerShape(100), border: BorderStroke? = null, - colors: ButtonColors = ButtonDefaults.buttonColors(), + colors: ButtonColors = ButtonDefaults.buttonColors( + backgroundColor = ExtendedTheme.colors.accent, + contentColor = ExtendedTheme.colors.onAccent + ), contentPadding: PaddingValues = ButtonDefaults.ContentPadding, content: @Composable RowScope.() -> Unit ) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f1b7f5cf..ee56f885 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -642,4 +642,10 @@ 在 Android 12 及以上版本中,后台启动前台服务被严格限制。为了避免一键签到无法正常启动,请忽略本应用的“电池优化”。 好的,去忽略 不再提示 + 链接 + 用户 + 这里什么都没有 + 请登录之后查看你的关注。\n或者你也可以先去逛逛发现? + 去登录 + 随便看看