State hoisting for FlowPage and ReadPage
This commit is contained in:
parent
ac5e68bf86
commit
4c95f89b07
|
@ -26,20 +26,20 @@ import me.ash.reader.ui.widget.ViewPager
|
|||
fun HomePage(
|
||||
navController: NavHostController,
|
||||
extrasArticleId: Any? = null,
|
||||
viewModel: HomeViewModel = hiltViewModel(),
|
||||
homeViewModel: HomeViewModel = hiltViewModel(),
|
||||
readViewModel: ReadViewModel = hiltViewModel(),
|
||||
feedOptionViewModel: FeedOptionViewModel = hiltViewModel(),
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val viewState = viewModel.viewState.collectAsStateValue()
|
||||
val filterState = viewModel.filterState.collectAsStateValue()
|
||||
val syncState = viewModel.syncState.collectAsStateValue()
|
||||
val viewState = homeViewModel.viewState.collectAsStateValue()
|
||||
val filterState = homeViewModel.filterState.collectAsStateValue()
|
||||
val syncState = homeViewModel.syncState.collectAsStateValue()
|
||||
|
||||
OpenArticleByExtras(extrasArticleId)
|
||||
|
||||
BackHandler(true) {
|
||||
val currentPage = viewState.pagerState.currentPage
|
||||
viewModel.dispatch(
|
||||
homeViewModel.dispatch(
|
||||
HomeViewAction.ScrollToPage(
|
||||
scope = scope,
|
||||
targetPage = when (currentPage) {
|
||||
|
@ -58,8 +58,8 @@ fun HomePage(
|
|||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(viewModel.viewState) {
|
||||
viewModel.viewState.collect {
|
||||
LaunchedEffect(homeViewModel.viewState) {
|
||||
homeViewModel.viewState.collect {
|
||||
Log.i(
|
||||
"RLog",
|
||||
"HomePage: ${it.pagerState.currentPage}, ${it.pagerState.targetPage}, ${it.pagerState.currentPageOffset}"
|
||||
|
@ -78,13 +78,13 @@ fun HomePage(
|
|||
filterState = filterState,
|
||||
syncState = syncState,
|
||||
onSyncClick = {
|
||||
viewModel.dispatch(HomeViewAction.Sync)
|
||||
homeViewModel.dispatch(HomeViewAction.Sync)
|
||||
},
|
||||
onFilterChange = {
|
||||
viewModel.dispatch(HomeViewAction.ChangeFilter(it))
|
||||
homeViewModel.dispatch(HomeViewAction.ChangeFilter(it))
|
||||
},
|
||||
onScrollToPage = {
|
||||
viewModel.dispatch(
|
||||
homeViewModel.dispatch(
|
||||
HomeViewAction.ScrollToPage(
|
||||
scope = scope,
|
||||
targetPage = it,
|
||||
|
@ -94,10 +94,47 @@ fun HomePage(
|
|||
)
|
||||
},
|
||||
{
|
||||
FlowPage(navController = navController)
|
||||
FlowPage(
|
||||
navController = navController,
|
||||
filterState = filterState,
|
||||
onScrollToPage = {
|
||||
homeViewModel.dispatch(
|
||||
HomeViewAction.ScrollToPage(
|
||||
scope = scope,
|
||||
targetPage = it,
|
||||
)
|
||||
)
|
||||
},
|
||||
onFilterChange = {
|
||||
homeViewModel.dispatch(HomeViewAction.ChangeFilter(it))
|
||||
},
|
||||
onItemClick = {
|
||||
readViewModel.dispatch(ReadViewAction.ScrollToItem(0))
|
||||
readViewModel.dispatch(ReadViewAction.InitData(it))
|
||||
if (it.feed.isFullContent) readViewModel.dispatch(ReadViewAction.RenderFullContent)
|
||||
else readViewModel.dispatch(ReadViewAction.RenderDescriptionContent)
|
||||
readViewModel.dispatch(ReadViewAction.RenderDescriptionContent)
|
||||
homeViewModel.dispatch(
|
||||
HomeViewAction.ScrollToPage(
|
||||
scope = scope,
|
||||
targetPage = 2,
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
{
|
||||
ReadPage(navController = navController)
|
||||
ReadPage(
|
||||
navController = navController,
|
||||
onScrollToPage = { targetPage, callback ->
|
||||
homeViewModel.dispatch(
|
||||
HomeViewAction.ScrollToPage(
|
||||
scope = scope,
|
||||
targetPage = targetPage,
|
||||
callback = callback
|
||||
),
|
||||
)
|
||||
})
|
||||
},
|
||||
),
|
||||
)
|
||||
|
|
|
@ -13,7 +13,6 @@ import androidx.compose.material.icons.rounded.Search
|
|||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
@ -25,14 +24,11 @@ import androidx.paging.LoadState
|
|||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.data.article.ArticleWithFeed
|
||||
import me.ash.reader.ui.extension.collectAsStateValue
|
||||
import me.ash.reader.ui.extension.getName
|
||||
import me.ash.reader.ui.page.home.FilterBar
|
||||
import me.ash.reader.ui.page.home.HomeViewAction
|
||||
import me.ash.reader.ui.page.home.HomeViewModel
|
||||
import me.ash.reader.ui.page.home.read.ReadViewAction
|
||||
import me.ash.reader.ui.page.home.read.ReadViewModel
|
||||
import me.ash.reader.ui.widget.LottieAnimation
|
||||
import me.ash.reader.ui.page.home.FilterState
|
||||
|
||||
@OptIn(
|
||||
ExperimentalMaterial3Api::class,
|
||||
|
@ -43,23 +39,22 @@ fun FlowPage(
|
|||
modifier: Modifier = Modifier,
|
||||
navController: NavHostController,
|
||||
flowViewModel: FlowViewModel = hiltViewModel(),
|
||||
homeViewModel: HomeViewModel = hiltViewModel(),
|
||||
readViewModel: ReadViewModel = hiltViewModel(),
|
||||
filterState: FilterState,
|
||||
onFilterChange: (filterState: FilterState) -> Unit = {},
|
||||
onScrollToPage: (targetPage: Int) -> Unit = {},
|
||||
onItemClick: (item: ArticleWithFeed) -> Unit = {},
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val viewState = flowViewModel.viewState.collectAsStateValue()
|
||||
val filterState = homeViewModel.filterState.collectAsStateValue()
|
||||
val pagingItems = viewState.pagingData.collectAsLazyPagingItems()
|
||||
var markAsRead by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(homeViewModel.filterState) {
|
||||
homeViewModel.filterState.collect { state ->
|
||||
LaunchedEffect(filterState) {
|
||||
flowViewModel.dispatch(
|
||||
FlowViewAction.FetchData(state)
|
||||
FlowViewAction.FetchData(filterState)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// LaunchedEffect(viewState.listState.isScrollInProgress) {
|
||||
// Log.i("RLog", "isScrollInProgress: ${viewState.listState.isScrollInProgress}")
|
||||
|
@ -81,14 +76,7 @@ fun FlowPage(
|
|||
SmallTopAppBar(
|
||||
title = {},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
homeViewModel.dispatch(
|
||||
HomeViewAction.ScrollToPage(
|
||||
scope = scope,
|
||||
targetPage = 0,
|
||||
)
|
||||
)
|
||||
}) {
|
||||
IconButton(onClick = { onScrollToPage(0) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back),
|
||||
|
@ -131,12 +119,14 @@ fun FlowPage(
|
|||
},
|
||||
content = {
|
||||
Crossfade(targetState = pagingItems) { pagingItems ->
|
||||
if (pagingItems.loadState.source.refresh is LoadState.NotLoading && pagingItems.itemCount == 0) {
|
||||
LottieAnimation(
|
||||
modifier = Modifier.alpha(0.7f).padding(80.dp),
|
||||
url = "https://assets7.lottiefiles.com/packages/lf20_l4ny0jjm.json",
|
||||
)
|
||||
}
|
||||
// if (pagingItems.loadState.source.refresh is LoadState.NotLoading && pagingItems.itemCount == 0) {
|
||||
// LottieAnimation(
|
||||
// modifier = Modifier
|
||||
// .alpha(0.7f)
|
||||
// .padding(80.dp),
|
||||
// url = "https://assets7.lottiefiles.com/packages/lf20_l4ny0jjm.json",
|
||||
// )
|
||||
// }
|
||||
LazyColumn(
|
||||
state = viewState.listState,
|
||||
) {
|
||||
|
@ -178,17 +168,7 @@ fun FlowPage(
|
|||
pagingItems = pagingItems,
|
||||
) {
|
||||
markAsRead = false
|
||||
readViewModel.dispatch(ReadViewAction.ScrollToItem(0))
|
||||
readViewModel.dispatch(ReadViewAction.InitData(it))
|
||||
if (it.feed.isFullContent) readViewModel.dispatch(ReadViewAction.RenderFullContent)
|
||||
else readViewModel.dispatch(ReadViewAction.RenderDescriptionContent)
|
||||
readViewModel.dispatch(ReadViewAction.RenderDescriptionContent)
|
||||
homeViewModel.dispatch(
|
||||
HomeViewAction.ScrollToPage(
|
||||
scope = scope,
|
||||
targetPage = 2,
|
||||
)
|
||||
)
|
||||
onItemClick(it)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(64.dp))
|
||||
|
@ -207,13 +187,11 @@ fun FlowPage(
|
|||
filter = filterState.filter,
|
||||
filterOnClick = {
|
||||
markAsRead = false
|
||||
homeViewModel.dispatch(
|
||||
HomeViewAction.ChangeFilter(
|
||||
onFilterChange(
|
||||
filterState.copy(
|
||||
filter = it
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package me.ash.reader.ui.page.home.read
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
@ -8,49 +7,49 @@ import androidx.compose.material3.MaterialTheme
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import me.ash.reader.data.article.Article
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import me.ash.reader.data.article.ArticleWithFeed
|
||||
import me.ash.reader.formatToString
|
||||
import me.ash.reader.ui.extension.roundClick
|
||||
|
||||
@Composable
|
||||
fun Header(
|
||||
context: Context,
|
||||
article: Article,
|
||||
feed: Feed
|
||||
articleWithFeed: ArticleWithFeed,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.roundClick {
|
||||
context.startActivity(
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(article.link))
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(articleWithFeed.article.link))
|
||||
)
|
||||
}
|
||||
.padding(12.dp)
|
||||
) {
|
||||
Text(
|
||||
text = article.date.formatToString(context, atHourMinute = true),
|
||||
text = articleWithFeed.article.date.formatToString(context, atHourMinute = true),
|
||||
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = article.title,
|
||||
text = articleWithFeed.article.title,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.headlineLarge,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
article.author?.let {
|
||||
articleWithFeed.article.author?.let {
|
||||
Text(
|
||||
text = article.author,
|
||||
text = articleWithFeed.article.author,
|
||||
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = feed.name,
|
||||
text = articleWithFeed.feed.name,
|
||||
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package me.ash.reader.ui.page.home.read
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Headphones
|
||||
import androidx.compose.material.icons.outlined.MoreVert
|
||||
|
@ -14,20 +15,14 @@ import androidx.compose.material3.*
|
|||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.data.article.ArticleWithFeed
|
||||
import me.ash.reader.ui.extension.collectAsStateValue
|
||||
import me.ash.reader.ui.page.home.HomeViewAction
|
||||
import me.ash.reader.ui.page.home.HomeViewModel
|
||||
import me.ash.reader.ui.widget.LottieAnimation
|
||||
import me.ash.reader.ui.widget.WebView
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
@ -36,10 +31,8 @@ fun ReadPage(
|
|||
navController: NavHostController,
|
||||
modifier: Modifier = Modifier,
|
||||
readViewModel: ReadViewModel = hiltViewModel(),
|
||||
homeViewModel: HomeViewModel = hiltViewModel(),
|
||||
onScrollToPage: (targetPage: Int, callback: () -> Unit) -> Unit = { _, _ -> },
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val viewState = readViewModel.viewState.collectAsStateValue()
|
||||
var isScrollDown by remember { mutableStateOf(false) }
|
||||
|
||||
|
@ -92,14 +85,18 @@ fun ReadPage(
|
|||
contentAlignment = Alignment.TopCenter
|
||||
) {
|
||||
TopBar(
|
||||
viewState.articleWithFeed == null || !isScrollDown,
|
||||
homeViewModel,
|
||||
scope,
|
||||
readViewModel,
|
||||
viewState
|
||||
isShow = viewState.articleWithFeed == null || !isScrollDown,
|
||||
onScrollToPage = onScrollToPage,
|
||||
onClearArticle = {
|
||||
readViewModel.dispatch(ReadViewAction.ClearArticle)
|
||||
}
|
||||
)
|
||||
}
|
||||
Content(viewState, viewState.articleWithFeed, context)
|
||||
Content(
|
||||
content = viewState.content ?: "",
|
||||
articleWithFeed = viewState.articleWithFeed,
|
||||
LazyListState = viewState.listState,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
|
@ -107,9 +104,18 @@ fun ReadPage(
|
|||
contentAlignment = Alignment.BottomCenter
|
||||
) {
|
||||
BottomBar(
|
||||
viewState.articleWithFeed != null && !isScrollDown,
|
||||
viewState.articleWithFeed,
|
||||
readViewModel
|
||||
isShow = viewState.articleWithFeed != null && !isScrollDown,
|
||||
articleWithFeed = viewState.articleWithFeed,
|
||||
unreadOnClick = {
|
||||
readViewModel.dispatch(ReadViewAction.MarkUnread(it))
|
||||
},
|
||||
starredOnClick = {
|
||||
readViewModel.dispatch(ReadViewAction.MarkStarred(it))
|
||||
},
|
||||
fullContentOnClick = { afterIsFullContent ->
|
||||
if (afterIsFullContent) readViewModel.dispatch(ReadViewAction.RenderFullContent)
|
||||
else readViewModel.dispatch(ReadViewAction.RenderDescriptionContent)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -121,10 +127,9 @@ fun ReadPage(
|
|||
@Composable
|
||||
private fun TopBar(
|
||||
isShow: Boolean,
|
||||
homeViewModel: HomeViewModel,
|
||||
scope: CoroutineScope,
|
||||
readViewModel: ReadViewModel,
|
||||
viewState: ReadViewState
|
||||
isShowActions: Boolean = false,
|
||||
onScrollToPage: (targetPage: Int, callback: () -> Unit) -> Unit = { _, _ -> },
|
||||
onClearArticle: () -> Unit = {},
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = isShow,
|
||||
|
@ -138,15 +143,9 @@ private fun TopBar(
|
|||
title = {},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
homeViewModel.dispatch(
|
||||
HomeViewAction.ScrollToPage(
|
||||
scope = scope,
|
||||
targetPage = 1,
|
||||
callback = {
|
||||
readViewModel.dispatch(ReadViewAction.ClearArticle)
|
||||
onScrollToPage(1) {
|
||||
onClearArticle()
|
||||
}
|
||||
)
|
||||
)
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Close,
|
||||
|
@ -156,7 +155,7 @@ private fun TopBar(
|
|||
}
|
||||
},
|
||||
actions = {
|
||||
viewState.articleWithFeed?.let {
|
||||
if (isShowActions) {
|
||||
IconButton(onClick = {}) {
|
||||
Icon(
|
||||
modifier = Modifier.size(22.dp),
|
||||
|
@ -178,60 +177,25 @@ private fun TopBar(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomBar(
|
||||
isShow: Boolean,
|
||||
articleWithFeed: ArticleWithFeed?,
|
||||
readViewModel: ReadViewModel
|
||||
) {
|
||||
articleWithFeed?.let {
|
||||
AnimatedVisibility(
|
||||
visible = isShow,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
ReadBar(
|
||||
disabled = false,
|
||||
isUnread = articleWithFeed.article.isUnread,
|
||||
isStarred = articleWithFeed.article.isStarred,
|
||||
isFullContent = articleWithFeed.feed.isFullContent,
|
||||
unreadOnClick = {
|
||||
readViewModel.dispatch(ReadViewAction.MarkUnread(it))
|
||||
},
|
||||
starredOnClick = {
|
||||
readViewModel.dispatch(ReadViewAction.MarkStarred(it))
|
||||
},
|
||||
fullContentOnClick = { afterIsFullContent ->
|
||||
if (afterIsFullContent) readViewModel.dispatch(ReadViewAction.RenderFullContent)
|
||||
else readViewModel.dispatch(ReadViewAction.RenderDescriptionContent)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(
|
||||
viewState: ReadViewState,
|
||||
content: String,
|
||||
articleWithFeed: ArticleWithFeed?,
|
||||
context: Context
|
||||
LazyListState: LazyListState = rememberLazyListState(),
|
||||
) {
|
||||
Column {
|
||||
if (articleWithFeed == null) {
|
||||
Spacer(modifier = Modifier.height(64.dp))
|
||||
LottieAnimation(
|
||||
modifier = Modifier
|
||||
.alpha(0.7f)
|
||||
.padding(80.dp),
|
||||
url = "https://assets8.lottiefiles.com/packages/lf20_jm7mv1ib.json",
|
||||
)
|
||||
// LottieAnimation(
|
||||
// modifier = Modifier
|
||||
// .alpha(0.7f)
|
||||
// .padding(80.dp),
|
||||
// url = "https://assets8.lottiefiles.com/packages/lf20_jm7mv1ib.json",
|
||||
// )
|
||||
} else {
|
||||
LazyColumn(
|
||||
state = viewState.listState,
|
||||
state = LazyListState,
|
||||
) {
|
||||
val article = articleWithFeed.article
|
||||
val feed = articleWithFeed.feed
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(64.dp))
|
||||
}
|
||||
|
@ -241,14 +205,14 @@ private fun Content(
|
|||
modifier = Modifier
|
||||
.padding(horizontal = 12.dp)
|
||||
) {
|
||||
Header(context, article, feed)
|
||||
Header(articleWithFeed)
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(22.dp))
|
||||
Crossfade(targetState = viewState.content) { content ->
|
||||
Crossfade(targetState = content) { content ->
|
||||
WebView(
|
||||
content = content ?: "",
|
||||
content = content
|
||||
)
|
||||
Spacer(modifier = Modifier.height(50.dp))
|
||||
}
|
||||
|
@ -262,3 +226,29 @@ private fun Content(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomBar(
|
||||
isShow: Boolean,
|
||||
articleWithFeed: ArticleWithFeed?,
|
||||
unreadOnClick: (afterIsUnread: Boolean) -> Unit = {},
|
||||
starredOnClick: (afterIsStarred: Boolean) -> Unit = {},
|
||||
fullContentOnClick: (afterIsFullContent: Boolean) -> Unit = {},
|
||||
) {
|
||||
articleWithFeed?.let {
|
||||
AnimatedVisibility(
|
||||
visible = isShow,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
ReadBar(
|
||||
disabled = false,
|
||||
isUnread = articleWithFeed.article.isUnread,
|
||||
isStarred = articleWithFeed.article.isStarred,
|
||||
isFullContent = articleWithFeed.feed.isFullContent,
|
||||
unreadOnClick = unreadOnClick,
|
||||
starredOnClick = starredOnClick,
|
||||
fullContentOnClick = fullContentOnClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user