diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt
index 3c5f91f..ef7ae7d 100644
--- a/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt
+++ b/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt
@@ -276,7 +276,7 @@ fun IndexScreen() {
bottomBar = {
NavigationBar(
modifier = Modifier.height(58.dp + navigationBarHeight),
- containerColor = AppColors.background
+ containerColor = AppColors.secondaryBackground
) {
item.forEachIndexed { idx, it ->
val isSelected = model.tabIndex == idx
diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/Moment.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/Moment.kt
index f1b3827..6cb0026 100644
--- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/Moment.kt
+++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/Moment.kt
@@ -47,6 +47,7 @@ import com.aiosman.ravenow.ui.index.tabs.moment.tabs.dynamic.Dynamic
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.Explore
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.hot.HotMomentsList
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentsList
+import com.aiosman.ravenow.ui.index.tabs.moment.tabs.news.NewsScreen
import com.aiosman.ravenow.ui.index.tabs.search.SearchViewModel
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import kotlinx.coroutines.launch
@@ -251,7 +252,7 @@ fun MomentsList() {
}
5 -> {
// 新闻页面
-
+ NewsScreen()
}
}
}
diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/news/NewsScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/news/NewsScreen.kt
new file mode 100644
index 0000000..08eb59b
--- /dev/null
+++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/news/NewsScreen.kt
@@ -0,0 +1,337 @@
+package com.aiosman.ravenow.ui.index.tabs.moment.tabs.news
+
+import androidx.compose.foundation.Image
+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.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+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.lazy.LazyColumn
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.pullrefresh.PullRefreshIndicator
+import androidx.compose.material.pullrefresh.pullRefresh
+import androidx.compose.material.pullrefresh.rememberPullRefreshState
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.aiosman.ravenow.LocalAppTheme
+import com.aiosman.ravenow.R
+import com.aiosman.ravenow.entity.MomentEntity
+import com.aiosman.ravenow.exp.timeAgo
+import com.aiosman.ravenow.ui.composables.CustomAsyncImage
+import com.aiosman.ravenow.ui.index.tabs.moment.tabs.dynamic.DynamicViewModel
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+fun NewsScreen() {
+ val model = DynamicViewModel
+ val moments = model.moments
+ val AppColors = LocalAppTheme.current
+ val context = LocalContext.current
+
+ // 下拉刷新状态
+ val state = rememberPullRefreshState(model.refreshing, onRefresh = {
+ model.refreshPager(pullRefresh = true)
+ })
+
+ // 列表状态
+ val listState = rememberLazyListState()
+
+ // 用于跟踪是否已经触发过加载更多
+ var hasTriggeredLoadMore by remember { mutableStateOf(false) }
+
+ // 监听滚动到底部
+ val reachedBottom by remember {
+ derivedStateOf {
+ val layoutInfo = listState.layoutInfo
+ val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull()
+ val totalItems = layoutInfo.totalItemsCount
+
+ if (lastVisibleItem == null || totalItems == 0) {
+ false
+ } else {
+ val isLastItemVisible = lastVisibleItem.index >= totalItems - 2
+ isLastItemVisible && !hasTriggeredLoadMore
+ }
+ }
+ }
+
+ // 加载更多数据
+ LaunchedEffect(reachedBottom) {
+ if (reachedBottom) {
+ hasTriggeredLoadMore = true
+ model.loadMore()
+ }
+ }
+
+ // 初始化加载数据
+ LaunchedEffect(Unit) {
+ model.refreshPager()
+ }
+
+ // 监听数据变化,重置加载状态
+ LaunchedEffect(moments.size) {
+ if (moments.size > 0) {
+ kotlinx.coroutines.delay(500)
+ hasTriggeredLoadMore = false
+ }
+ }
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(AppColors.background)
+ ) {
+ Box(Modifier.pullRefresh(state)) {
+ LazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ state = listState
+ ) {
+ items(
+ moments.size,
+ key = { idx -> idx }
+ ) { idx ->
+ // 处理下标越界
+ if (idx < 0 || idx >= moments.size) return@items
+ val momentItem = moments[idx] ?: return@items
+
+ NewsItem(
+ moment = momentItem,
+ modifier = Modifier.fillMaxWidth()
+ )
+ }
+ }
+ PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
+ }
+ }
+}
+
+//单个新闻项
+@Composable
+fun NewsItem(
+ moment: MomentEntity,
+ modifier: Modifier = Modifier
+) {
+ val AppColors = LocalAppTheme.current
+ val context = LocalContext.current
+
+ Column(
+ modifier = modifier
+ .fillMaxWidth()
+ .height(700.dp)
+ .background(AppColors.background)
+ .padding(vertical = 8.dp),
+ verticalArrangement = Arrangement.SpaceBetween
+ ) {
+ Column {
+ // 新闻图片
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(300.dp)
+ .padding(horizontal = 16.dp)
+ ) {
+ if (moment.images.isNotEmpty()) {
+ CustomAsyncImage(
+ context = context,
+ imageUrl = moment.images[0].thumbnail,
+ contentDescription = "新闻图片",
+ contentScale = ContentScale.Crop,
+ blurHash = moment.images[0].blurHash,
+ modifier = Modifier
+ .fillMaxSize()
+ .clip(RoundedCornerShape(8.dp))
+ )
+ } else {
+ Image(
+ painter = androidx.compose.ui.res.painterResource(id = R.drawable.default_moment_img),
+ contentDescription = "默认图片",
+ contentScale = ContentScale.Crop,
+ modifier = Modifier
+ .fillMaxSize()
+ .clip(RoundedCornerShape(8.dp))
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // 新闻标题
+ Text(
+ text = moment.nickname, // 暂时使用用户名作为标题
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Bold,
+ color = AppColors.text,
+ maxLines = 2,
+ overflow = TextOverflow.Ellipsis
+ )
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // 新闻内容
+ Text(
+ text = moment.momentTextContent, // 使用动态内容作为新闻内容
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ fontSize = 14.sp,
+ color = AppColors.text,
+ lineHeight = 20.sp
+ )
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // 新闻信息
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ // 来源和时间
+ Text(
+ text = "${moment.nickname} • ${moment.time.timeAgo(context)}",
+ fontSize = 12.sp,
+ color = AppColors.secondaryText
+ )
+
+ // 查看全文
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = stringResource(R.string.read_full_article),
+ fontSize = 12.sp,
+ color = AppColors.text
+ )
+ Spacer(modifier = Modifier.width(4.dp))
+ Image(
+ painter = androidx.compose.ui.res.painterResource(id = R.drawable.rider_pro_nav_search),
+ contentDescription = "箭头",
+ modifier = Modifier.size(12.dp),
+ colorFilter = ColorFilter.tint(AppColors.text)
+ )
+ }
+ }
+ }
+
+ // 互动栏
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp)
+ .padding(bottom = 50.dp),
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ // 点赞
+ NewsActionButton(
+ icon = R.drawable.rider_pro_moment_like,
+ count = moment.likeCount.toString(),
+ isActive = moment.liked
+ )
+
+ // 评论
+ NewsActionButton(
+ icon = R.mipmap.icon_comment,
+ count = moment.commentCount.toString(),
+ isActive = false
+ )
+
+ // 收藏
+ NewsActionButton(
+ icon = R.mipmap.icon_collect,
+ count = moment.favoriteCount.toString(),
+ isActive = moment.isFavorite
+ )
+
+ // 分享
+ NewsActionButton(
+ icon = R.mipmap.icon_share,
+ count = "",
+ isActive = false,
+ text = stringResource(R.string.share),
+ textSize = 8.sp
+ )
+ }
+ }
+}
+
+// 互动栏按钮
+@Composable
+fun NewsActionButton(
+ icon: Int,
+ count: String,
+ isActive: Boolean,
+ modifier: Modifier = Modifier,
+ text: String? = null,
+ textSize: androidx.compose.ui.unit.TextUnit = 12.sp
+) {
+ val AppColors = LocalAppTheme.current
+
+ Row(
+ modifier = modifier
+ .width(60.dp)
+ .background(
+ color = AppColors.secondaryBackground,
+ shape = RoundedCornerShape(16.dp)
+ )
+ .padding(horizontal = 12.dp, vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Image(
+ painter = androidx.compose.ui.res.painterResource(id = icon),
+ contentDescription = "操作图标",
+ modifier = Modifier.size(16.dp),
+ colorFilter = ColorFilter.tint(
+ if (isActive) AppColors.background else AppColors.text
+ )
+ )
+
+ if (count.isNotEmpty()) {
+ Spacer(modifier = Modifier.width(4.dp))
+ Text(
+ text = count,
+ fontSize = 12.sp,
+ color = if (isActive) AppColors.background else AppColors.text
+ )
+ }
+ if (text != null) {
+ Spacer(modifier = Modifier.width(4.dp))
+ Text(
+ text = text,
+ fontSize = textSize,
+ color = AppColors.text
+ )
+ }
+ }
+}
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 7b7524b..6c8e612 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -193,6 +193,7 @@
発見
パスワードは%1$d文字を超えることはできません
ブロック
+ 全文を読む
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index 3ef7e5e..1aa274d 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -196,6 +196,7 @@
密码不能超过 %1$d 个字符
人正在热聊…
拉黑
+ 查看全文
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7eb604d..3b44081 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -192,6 +192,7 @@
Discover
Password cannot exceed %1$d characters
Block
+ Read full article
Create