feat: 热榜页面贴子卡片 & 点赞
This commit is contained in:
parent
dff92beadc
commit
a5c32056c0
|
|
@ -57,6 +57,12 @@ val ThreadInfo.abstractText: String
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val ThreadInfo.hasAgree: Int
|
||||||
|
get() = agree?.hasAgree ?: 0
|
||||||
|
|
||||||
|
val ThreadInfo.hasAgreed: Boolean
|
||||||
|
get() = hasAgree == 1
|
||||||
|
|
||||||
val ThreadInfo.hasAbstract: Boolean
|
val ThreadInfo.hasAbstract: Boolean
|
||||||
get() = richAbstract.any { (it.type == 0 && it.text.isNotBlank()) || it.type == 2 }
|
get() = richAbstract.any { (it.type == 0 && it.text.isNotBlank()) || it.type == 2 }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
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
|
||||||
|
|
@ -45,7 +46,9 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.google.accompanist.placeholder.material.placeholder
|
import com.google.accompanist.placeholder.material.placeholder
|
||||||
import com.huanchengfly.tieba.post.R
|
import com.huanchengfly.tieba.post.R
|
||||||
|
import com.huanchengfly.tieba.post.api.hasAgree
|
||||||
import com.huanchengfly.tieba.post.api.models.protos.ThreadInfo
|
import com.huanchengfly.tieba.post.api.models.protos.ThreadInfo
|
||||||
|
import com.huanchengfly.tieba.post.arch.ImmutableHolder
|
||||||
import com.huanchengfly.tieba.post.arch.collectPartialAsState
|
import com.huanchengfly.tieba.post.arch.collectPartialAsState
|
||||||
import com.huanchengfly.tieba.post.arch.onEvent
|
import com.huanchengfly.tieba.post.arch.onEvent
|
||||||
import com.huanchengfly.tieba.post.arch.pageViewModel
|
import com.huanchengfly.tieba.post.arch.pageViewModel
|
||||||
|
|
@ -56,7 +59,9 @@ import com.huanchengfly.tieba.post.ui.common.theme.compose.White
|
||||||
import com.huanchengfly.tieba.post.ui.common.theme.compose.Yellow
|
import com.huanchengfly.tieba.post.ui.common.theme.compose.Yellow
|
||||||
import com.huanchengfly.tieba.post.ui.page.LocalNavigator
|
import com.huanchengfly.tieba.post.ui.page.LocalNavigator
|
||||||
import com.huanchengfly.tieba.post.ui.page.destinations.HotTopicListPageDestination
|
import com.huanchengfly.tieba.post.ui.page.destinations.HotTopicListPageDestination
|
||||||
|
import com.huanchengfly.tieba.post.ui.page.destinations.ThreadPageDestination
|
||||||
import com.huanchengfly.tieba.post.ui.page.main.MainUiEvent
|
import com.huanchengfly.tieba.post.ui.page.main.MainUiEvent
|
||||||
|
import com.huanchengfly.tieba.post.ui.widgets.compose.FeedCard
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
|
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.NetworkImage
|
import com.huanchengfly.tieba.post.ui.widgets.compose.NetworkImage
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.ProvideContentColor
|
import com.huanchengfly.tieba.post.ui.widgets.compose.ProvideContentColor
|
||||||
|
|
@ -155,12 +160,12 @@ fun HotPage(
|
||||||
modifier = Modifier.padding(bottom = 2.dp)
|
modifier = Modifier.padding(bottom = 2.dp)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = item.topicName,
|
text = item.get { topicName },
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
when (item.tag) {
|
when (item.get { tag }) {
|
||||||
2 -> Text(
|
2 -> Text(
|
||||||
text = stringResource(id = R.string.topic_tag_hot),
|
text = stringResource(id = R.string.topic_tag_hot),
|
||||||
fontSize = 10.sp,
|
fontSize = 10.sp,
|
||||||
|
|
@ -220,7 +225,7 @@ fun HotPage(
|
||||||
}
|
}
|
||||||
if (threadList.isNotEmpty()) {
|
if (threadList.isNotEmpty()) {
|
||||||
if (tabList.isNotEmpty()) {
|
if (tabList.isNotEmpty()) {
|
||||||
stickyHeader(key = "ThreadTabs") {
|
item(key = "ThreadTabs") {
|
||||||
VerticalGrid(
|
VerticalGrid(
|
||||||
column = 5,
|
column = 5,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -239,9 +244,9 @@ fun HotPage(
|
||||||
}
|
}
|
||||||
items(tabList) {
|
items(tabList) {
|
||||||
ThreadListTab(
|
ThreadListTab(
|
||||||
text = it.tabName,
|
text = it.get { tabName },
|
||||||
selected = currentTabCode == it.tabCode,
|
selected = currentTabCode == it.get { tabCode },
|
||||||
onSelected = { viewModel.send(HotUiIntent.RefreshThreadList(it.tabCode)) }
|
onSelected = { viewModel.send(HotUiIntent.RefreshThreadList(it.get { tabCode })) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -266,8 +271,68 @@ fun HotPage(
|
||||||
} else {
|
} else {
|
||||||
itemsIndexed(
|
itemsIndexed(
|
||||||
items = threadList,
|
items = threadList,
|
||||||
key = { _, item -> "Thread_${item.threadId}" }) { index, item ->
|
key = { _, item -> "Thread_${item.get { threadId }}" }
|
||||||
ThreadListItem(index = index, item = item)
|
) { index, item ->
|
||||||
|
FeedCard(
|
||||||
|
item = item,
|
||||||
|
onClick = {
|
||||||
|
navigator.navigate(
|
||||||
|
ThreadPageDestination(
|
||||||
|
threadId = it.id,
|
||||||
|
threadInfo = it
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onAgree = {
|
||||||
|
viewModel.send(
|
||||||
|
HotUiIntent.Agree(
|
||||||
|
threadId = it.threadId,
|
||||||
|
postId = it.firstPostId,
|
||||||
|
hasAgree = it.hasAgree
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.End,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
val color = when (index) {
|
||||||
|
0 -> RedA700
|
||||||
|
1 -> OrangeA700
|
||||||
|
2 -> Yellow
|
||||||
|
else -> MaterialTheme.colors.onBackground.copy(
|
||||||
|
ContentAlpha.medium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = "${index + 1}",
|
||||||
|
color = color,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(
|
||||||
|
id = R.string.hot_num,
|
||||||
|
item.get { hotNum }.getShortNumString()
|
||||||
|
),
|
||||||
|
style = MaterialTheme.typography.caption,
|
||||||
|
color = color
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ThreadListItem(
|
||||||
|
// index = index,
|
||||||
|
// itemHolder = item,
|
||||||
|
// onClick = {
|
||||||
|
// navigator.navigate(
|
||||||
|
// ThreadPageDestination(
|
||||||
|
// threadId = it.id,
|
||||||
|
// threadInfo = it
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -322,13 +387,14 @@ private fun ThreadListItemPlaceholder() {
|
||||||
@Composable
|
@Composable
|
||||||
private fun ThreadListItem(
|
private fun ThreadListItem(
|
||||||
index: Int,
|
index: Int,
|
||||||
item: ThreadInfo,
|
itemHolder: ImmutableHolder<ThreadInfo>,
|
||||||
onClick: () -> Unit = {}
|
onClick: (ThreadInfo) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
|
val item = remember(itemHolder) { itemHolder.get() }
|
||||||
val heightModifier = if (item.media.isEmpty()) Modifier else Modifier.height(80.dp)
|
val heightModifier = if (item.media.isEmpty()) Modifier else Modifier.height(80.dp)
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable(onClick = onClick)
|
.clickable { onClick(item) }
|
||||||
.padding(all = 16.dp),
|
.padding(all = 16.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
|
|
@ -368,7 +434,14 @@ private fun ThreadListItem(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.media.isNotEmpty()) {
|
if (!item.videoInfo?.thumbnailUrl.isNullOrBlank()) {
|
||||||
|
NetworkImage(
|
||||||
|
imageUri = item.videoInfo?.thumbnailUrl!!,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = heightModifier.aspectRatio(16f / 9),
|
||||||
|
contentScale = ContentScale.Crop
|
||||||
|
)
|
||||||
|
} else if (!item.media.firstOrNull()?.dynamicPic.isNullOrBlank()) {
|
||||||
NetworkImage(
|
NetworkImage(
|
||||||
imageUri = item.media.first().dynamicPic,
|
imageUri = item.media.first().dynamicPic,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
|
|
@ -397,7 +470,7 @@ private fun ThreadListTab(
|
||||||
.clip(RoundedCornerShape(100))
|
.clip(RoundedCornerShape(100))
|
||||||
.background(backgroundColor)
|
.background(backgroundColor)
|
||||||
.clickable(onClick = onSelected)
|
.clickable(onClick = onSelected)
|
||||||
.padding(horizontal = 16.dp, vertical = 4.dp),
|
.padding(vertical = 4.dp),
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,22 @@ package com.huanchengfly.tieba.post.ui.page.main.explore.hot
|
||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import com.huanchengfly.tieba.post.api.TiebaApi
|
import com.huanchengfly.tieba.post.api.TiebaApi
|
||||||
|
import com.huanchengfly.tieba.post.api.models.AgreeBean
|
||||||
import com.huanchengfly.tieba.post.api.models.protos.FrsTabInfo
|
import com.huanchengfly.tieba.post.api.models.protos.FrsTabInfo
|
||||||
import com.huanchengfly.tieba.post.api.models.protos.RecommendTopicList
|
import com.huanchengfly.tieba.post.api.models.protos.RecommendTopicList
|
||||||
import com.huanchengfly.tieba.post.api.models.protos.ThreadInfo
|
import com.huanchengfly.tieba.post.api.models.protos.ThreadInfo
|
||||||
import com.huanchengfly.tieba.post.api.models.protos.hotThreadList.HotThreadListResponse
|
import com.huanchengfly.tieba.post.api.models.protos.hotThreadList.HotThreadListResponse
|
||||||
import com.huanchengfly.tieba.post.arch.BaseViewModel
|
import com.huanchengfly.tieba.post.arch.BaseViewModel
|
||||||
|
import com.huanchengfly.tieba.post.arch.ImmutableHolder
|
||||||
import com.huanchengfly.tieba.post.arch.PartialChange
|
import com.huanchengfly.tieba.post.arch.PartialChange
|
||||||
import com.huanchengfly.tieba.post.arch.PartialChangeProducer
|
import com.huanchengfly.tieba.post.arch.PartialChangeProducer
|
||||||
import com.huanchengfly.tieba.post.arch.UiEvent
|
import com.huanchengfly.tieba.post.arch.UiEvent
|
||||||
import com.huanchengfly.tieba.post.arch.UiIntent
|
import com.huanchengfly.tieba.post.arch.UiIntent
|
||||||
import com.huanchengfly.tieba.post.arch.UiState
|
import com.huanchengfly.tieba.post.arch.UiState
|
||||||
|
import com.huanchengfly.tieba.post.arch.wrapImmutable
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
|
|
@ -41,6 +46,8 @@ class HotViewModel @Inject constructor() :
|
||||||
.flatMapConcat { produceLoadPartialChange() },
|
.flatMapConcat { produceLoadPartialChange() },
|
||||||
intentFlow.filterIsInstance<HotUiIntent.RefreshThreadList>()
|
intentFlow.filterIsInstance<HotUiIntent.RefreshThreadList>()
|
||||||
.flatMapConcat { it.producePartialChange() },
|
.flatMapConcat { it.producePartialChange() },
|
||||||
|
intentFlow.filterIsInstance<HotUiIntent.Agree>()
|
||||||
|
.flatMapConcat { it.producePartialChange() },
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun produceLoadPartialChange(): Flow<HotPartialChange.Load> =
|
private fun produceLoadPartialChange(): Flow<HotPartialChange.Load> =
|
||||||
|
|
@ -65,6 +72,20 @@ class HotViewModel @Inject constructor() :
|
||||||
}
|
}
|
||||||
.onStart { emit(HotPartialChange.RefreshThreadList.Start(tabCode)) }
|
.onStart { emit(HotPartialChange.RefreshThreadList.Start(tabCode)) }
|
||||||
.catch { emit(HotPartialChange.RefreshThreadList.Failure(tabCode, it)) }
|
.catch { emit(HotPartialChange.RefreshThreadList.Failure(tabCode, it)) }
|
||||||
|
|
||||||
|
private fun HotUiIntent.Agree.producePartialChange(): Flow<HotPartialChange.Agree> =
|
||||||
|
TiebaApi.getInstance()
|
||||||
|
.opAgreeFlow(
|
||||||
|
threadId.toString(), postId.toString(), hasAgree, objType = 3
|
||||||
|
)
|
||||||
|
.map<AgreeBean, HotPartialChange.Agree> {
|
||||||
|
HotPartialChange.Agree.Success(
|
||||||
|
threadId,
|
||||||
|
hasAgree xor 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.onStart { emit(HotPartialChange.Agree.Start(threadId, hasAgree xor 1)) }
|
||||||
|
.catch { emit(HotPartialChange.Agree.Failure(threadId, hasAgree, it)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,6 +93,12 @@ sealed interface HotUiIntent : UiIntent {
|
||||||
object Load : HotUiIntent
|
object Load : HotUiIntent
|
||||||
|
|
||||||
data class RefreshThreadList(val tabCode: String) : HotUiIntent
|
data class RefreshThreadList(val tabCode: String) : HotUiIntent
|
||||||
|
|
||||||
|
data class Agree(
|
||||||
|
val threadId: Long,
|
||||||
|
val postId: Long,
|
||||||
|
val hasAgree: Int
|
||||||
|
) : HotUiIntent
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface HotPartialChange : PartialChange<HotUiState> {
|
sealed interface HotPartialChange : PartialChange<HotUiState> {
|
||||||
|
|
@ -81,9 +108,10 @@ sealed interface HotPartialChange : PartialChange<HotUiState> {
|
||||||
Start -> oldState.copy(isRefreshing = true)
|
Start -> oldState.copy(isRefreshing = true)
|
||||||
is Success -> oldState.copy(
|
is Success -> oldState.copy(
|
||||||
isRefreshing = false,
|
isRefreshing = false,
|
||||||
topicList = topicList,
|
currentTabCode = "all",
|
||||||
tabList = tabList,
|
topicList = topicList.wrapImmutable(),
|
||||||
threadList = threadList
|
tabList = tabList.wrapImmutable(),
|
||||||
|
threadList = threadList.wrapImmutable()
|
||||||
)
|
)
|
||||||
|
|
||||||
is Failure -> oldState.copy(isRefreshing = false)
|
is Failure -> oldState.copy(isRefreshing = false)
|
||||||
|
|
@ -109,7 +137,7 @@ sealed interface HotPartialChange : PartialChange<HotUiState> {
|
||||||
is Success -> oldState.copy(
|
is Success -> oldState.copy(
|
||||||
isLoadingThreadList = false,
|
isLoadingThreadList = false,
|
||||||
currentTabCode = tabCode,
|
currentTabCode = tabCode,
|
||||||
threadList = threadList
|
threadList = threadList.wrapImmutable()
|
||||||
)
|
)
|
||||||
|
|
||||||
is Failure -> oldState.copy(isLoadingThreadList = false)
|
is Failure -> oldState.copy(isLoadingThreadList = false)
|
||||||
|
|
@ -127,15 +155,105 @@ sealed interface HotPartialChange : PartialChange<HotUiState> {
|
||||||
val error: Throwable
|
val error: Throwable
|
||||||
) : RefreshThreadList()
|
) : RefreshThreadList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class Agree private constructor() : HotPartialChange {
|
||||||
|
private fun List<ImmutableHolder<ThreadInfo>>.updateAgreeStatus(
|
||||||
|
threadId: Long,
|
||||||
|
hasAgree: Int
|
||||||
|
): ImmutableList<ImmutableHolder<ThreadInfo>> {
|
||||||
|
return map {
|
||||||
|
val threadInfo = it.get()
|
||||||
|
if (threadInfo.threadId == threadId) {
|
||||||
|
if (threadInfo.agree != null) {
|
||||||
|
if (hasAgree != threadInfo.agree.hasAgree) {
|
||||||
|
if (hasAgree == 1) {
|
||||||
|
threadInfo.copy(
|
||||||
|
agreeNum = threadInfo.agreeNum + 1,
|
||||||
|
agree = threadInfo.agree.copy(
|
||||||
|
agreeNum = threadInfo.agree.agreeNum + 1,
|
||||||
|
diffAgreeNum = threadInfo.agree.diffAgreeNum + 1,
|
||||||
|
hasAgree = 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
threadInfo.copy(
|
||||||
|
agreeNum = threadInfo.agreeNum - 1,
|
||||||
|
agree = threadInfo.agree.copy(
|
||||||
|
agreeNum = threadInfo.agree.agreeNum - 1,
|
||||||
|
diffAgreeNum = threadInfo.agree.diffAgreeNum - 1,
|
||||||
|
hasAgree = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
threadInfo
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
threadInfo.copy(
|
||||||
|
agreeNum = if (hasAgree == 1) threadInfo.agreeNum + 1 else threadInfo.agreeNum - 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
threadInfo
|
||||||
|
}
|
||||||
|
}.wrapImmutable()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reduce(oldState: HotUiState): HotUiState =
|
||||||
|
when (this) {
|
||||||
|
is Start -> {
|
||||||
|
oldState.copy(
|
||||||
|
threadList = oldState.threadList.updateAgreeStatus(
|
||||||
|
threadId,
|
||||||
|
hasAgree
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is Success -> {
|
||||||
|
oldState.copy(
|
||||||
|
threadList = oldState.threadList.updateAgreeStatus(
|
||||||
|
threadId,
|
||||||
|
hasAgree
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is Failure -> {
|
||||||
|
oldState.copy(
|
||||||
|
threadList = oldState.threadList.updateAgreeStatus(
|
||||||
|
threadId,
|
||||||
|
hasAgree
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Start(
|
||||||
|
val threadId: Long,
|
||||||
|
val hasAgree: Int
|
||||||
|
) : Agree()
|
||||||
|
|
||||||
|
data class Success(
|
||||||
|
val threadId: Long,
|
||||||
|
val hasAgree: Int
|
||||||
|
) : Agree()
|
||||||
|
|
||||||
|
data class Failure(
|
||||||
|
val threadId: Long,
|
||||||
|
val hasAgree: Int,
|
||||||
|
val error: Throwable
|
||||||
|
) : Agree()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class HotUiState(
|
data class HotUiState(
|
||||||
val isRefreshing: Boolean = true,
|
val isRefreshing: Boolean = true,
|
||||||
val currentTabCode: String = "all",
|
val currentTabCode: String = "all",
|
||||||
val isLoadingThreadList: Boolean = false,
|
val isLoadingThreadList: Boolean = false,
|
||||||
val topicList: List<RecommendTopicList> = emptyList(),
|
val topicList: ImmutableList<ImmutableHolder<RecommendTopicList>> = persistentListOf(),
|
||||||
val tabList: List<FrsTabInfo> = emptyList(),
|
val tabList: ImmutableList<ImmutableHolder<FrsTabInfo>> = persistentListOf(),
|
||||||
val threadList: List<ThreadInfo> = emptyList(),
|
val threadList: ImmutableList<ImmutableHolder<ThreadInfo>> = persistentListOf(),
|
||||||
) : UiState
|
) : UiState
|
||||||
|
|
||||||
sealed interface HotUiEvent : UiEvent
|
sealed interface HotUiEvent : UiEvent
|
||||||
|
|
@ -114,14 +114,16 @@ class PersonalizedViewModel @Inject constructor() :
|
||||||
.onStart { emit(PersonalizedPartialChange.Dislike.Start(threadId)) }
|
.onStart { emit(PersonalizedPartialChange.Dislike.Start(threadId)) }
|
||||||
|
|
||||||
private fun PersonalizedUiIntent.Agree.producePartialChange(): Flow<PersonalizedPartialChange.Agree> =
|
private fun PersonalizedUiIntent.Agree.producePartialChange(): Flow<PersonalizedPartialChange.Agree> =
|
||||||
TiebaApi.getInstance().opAgreeFlow(
|
TiebaApi.getInstance()
|
||||||
threadId.toString(), postId.toString(), hasAgree, objType = 3
|
.opAgreeFlow(
|
||||||
).map<AgreeBean, PersonalizedPartialChange.Agree> {
|
threadId.toString(), postId.toString(), hasAgree, objType = 3
|
||||||
PersonalizedPartialChange.Agree.Success(
|
|
||||||
threadId,
|
|
||||||
hasAgree xor 1
|
|
||||||
)
|
)
|
||||||
}
|
.map<AgreeBean, PersonalizedPartialChange.Agree> {
|
||||||
|
PersonalizedPartialChange.Agree.Success(
|
||||||
|
threadId,
|
||||||
|
hasAgree xor 1
|
||||||
|
)
|
||||||
|
}
|
||||||
.catch { emit(PersonalizedPartialChange.Agree.Failure(threadId, hasAgree, it)) }
|
.catch { emit(PersonalizedPartialChange.Agree.Failure(threadId, hasAgree, it)) }
|
||||||
.onStart { emit(PersonalizedPartialChange.Agree.Start(threadId, hasAgree xor 1)) }
|
.onStart { emit(PersonalizedPartialChange.Agree.Start(threadId, hasAgree xor 1)) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -485,7 +485,7 @@ private fun ThreadShareBtn(
|
||||||
fun FeedCard(
|
fun FeedCard(
|
||||||
item: ImmutableHolder<ThreadInfo>,
|
item: ImmutableHolder<ThreadInfo>,
|
||||||
onClick: (ThreadInfo) -> Unit,
|
onClick: (ThreadInfo) -> Unit,
|
||||||
onAgree: () -> Unit,
|
onAgree: (ThreadInfo) -> Unit,
|
||||||
onClickForum: () -> Unit = {},
|
onClickForum: () -> Unit = {},
|
||||||
dislikeAction: @Composable () -> Unit = {},
|
dislikeAction: @Composable () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
|
|
@ -526,7 +526,7 @@ fun FeedCard(
|
||||||
ThreadAgreeBtn(
|
ThreadAgreeBtn(
|
||||||
hasAgree = item.get { agree?.hasAgree == 1 },
|
hasAgree = item.get { agree?.hasAgree == 1 },
|
||||||
agreeNum = item.get { agreeNum },
|
agreeNum = item.get { agreeNum },
|
||||||
onClick = onAgree,
|
onClick = { onAgree(item.get()) },
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue