mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-03-26 17:52:29 +01:00
No more blinking in Feeds
This commit is contained in:
parent
ae82c690ea
commit
1e3654396b
@ -1,5 +1,6 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
|
||||
abstract class Card() {
|
||||
@ -37,7 +38,7 @@ class BoostSetCard(val note: Note, val boostEvents: List<Note>): Card() {
|
||||
|
||||
sealed class CardFeedState {
|
||||
object Loading: CardFeedState()
|
||||
class Loaded(val feed: List<Card>): CardFeedState()
|
||||
class Loaded(val feed: MutableState<List<Card>>): CardFeedState()
|
||||
object Empty: CardFeedState()
|
||||
class FeedError(val errorMessage: String): CardFeedState()
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -30,8 +31,6 @@ fun CardFeedView(viewModel: CardFeedViewModel, accountViewModel: AccountViewMode
|
||||
var isRefreshing by remember { mutableStateOf(false) }
|
||||
val swipeRefreshState = rememberSwipeRefreshState(isRefreshing)
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LaunchedEffect(isRefreshing) {
|
||||
if (isRefreshing) {
|
||||
viewModel.refresh()
|
||||
@ -59,21 +58,12 @@ fun CardFeedView(viewModel: CardFeedViewModel, accountViewModel: AccountViewMode
|
||||
}
|
||||
}
|
||||
is CardFeedState.Loaded -> {
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(
|
||||
top = 10.dp,
|
||||
bottom = 10.dp
|
||||
),
|
||||
state = listState
|
||||
) {
|
||||
itemsIndexed(state.feed, key = { _, item -> item.id() }) { index, item ->
|
||||
when (item) {
|
||||
is NoteCard -> NoteCompose(item.note, isInnerNote = false, accountViewModel = accountViewModel, navController = navController, routeForLastRead = routeForLastRead)
|
||||
is LikeSetCard -> LikeSetCompose(item, isInnerNote = false, accountViewModel = accountViewModel, navController = navController, routeForLastRead = routeForLastRead)
|
||||
is BoostSetCard -> BoostSetCompose(item, isInnerNote = false, accountViewModel = accountViewModel, navController = navController, routeForLastRead = routeForLastRead)
|
||||
}
|
||||
}
|
||||
}
|
||||
FeedLoaded(
|
||||
state,
|
||||
accountViewModel,
|
||||
navController,
|
||||
routeForLastRead
|
||||
)
|
||||
}
|
||||
CardFeedState.Loading -> {
|
||||
LoadingFeed()
|
||||
@ -83,3 +73,47 @@ fun CardFeedView(viewModel: CardFeedViewModel, accountViewModel: AccountViewMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeedLoaded(
|
||||
state: CardFeedState.Loaded,
|
||||
accountViewModel: AccountViewModel,
|
||||
navController: NavController,
|
||||
routeForLastRead: String
|
||||
) {
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(
|
||||
top = 10.dp,
|
||||
bottom = 10.dp
|
||||
),
|
||||
state = listState
|
||||
) {
|
||||
itemsIndexed(state.feed.value, key = { _, item -> item.id() }) { index, item ->
|
||||
when (item) {
|
||||
is NoteCard -> NoteCompose(
|
||||
item.note,
|
||||
isInnerNote = false,
|
||||
accountViewModel = accountViewModel,
|
||||
navController = navController,
|
||||
routeForLastRead = routeForLastRead
|
||||
)
|
||||
is LikeSetCard -> LikeSetCompose(
|
||||
item,
|
||||
isInnerNote = false,
|
||||
accountViewModel = accountViewModel,
|
||||
navController = navController,
|
||||
routeForLastRead = routeForLastRead
|
||||
)
|
||||
is BoostSetCard -> BoostSetCompose(
|
||||
item,
|
||||
isInnerNote = false,
|
||||
accountViewModel = accountViewModel,
|
||||
navController = navController,
|
||||
routeForLastRead = routeForLastRead
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.ui.screen
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
@ -43,7 +44,7 @@ class CardFeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel() {
|
||||
val newCards = convertToCard(notes.minus(lastNotesCopy))
|
||||
if (newCards.isNotEmpty()) {
|
||||
lastNotes = notes
|
||||
updateFeed((oldNotesState.feed + newCards).sortedBy { it.createdAt() }.reversed())
|
||||
updateFeed((oldNotesState.feed.value + newCards).sortedBy { it.createdAt() }.reversed())
|
||||
}
|
||||
} else {
|
||||
val cards = convertToCard(notes)
|
||||
@ -83,14 +84,20 @@ class CardFeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel() {
|
||||
fun updateFeed(notes: List<Card>) {
|
||||
val scope = CoroutineScope(Job() + Dispatchers.Main)
|
||||
scope.launch {
|
||||
val currentState = feedContent.value
|
||||
|
||||
if (notes.isEmpty()) {
|
||||
_feedContent.update { CardFeedState.Empty }
|
||||
} else if (currentState is CardFeedState.Loaded) {
|
||||
// updates the current list
|
||||
currentState.feed.value = notes
|
||||
} else {
|
||||
_feedContent.update { CardFeedState.Loaded(notes) }
|
||||
_feedContent.update { CardFeedState.Loaded(mutableStateOf(notes)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var handlerWaiting = false
|
||||
fun invalidateData() {
|
||||
synchronized(handlerWaiting) {
|
||||
|
@ -25,7 +25,6 @@ fun ChatroomFeedView(viewModel: FeedViewModel, accountViewModel: AccountViewMode
|
||||
val feedState by viewModel.feedContent.collectAsState()
|
||||
|
||||
var isRefreshing by remember { mutableStateOf(false) }
|
||||
val swipeRefreshState = rememberSwipeRefreshState(isRefreshing)
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
@ -36,43 +35,36 @@ fun ChatroomFeedView(viewModel: FeedViewModel, accountViewModel: AccountViewMode
|
||||
}
|
||||
}
|
||||
|
||||
SwipeRefresh(
|
||||
state = swipeRefreshState,
|
||||
onRefresh = {
|
||||
isRefreshing = true
|
||||
},
|
||||
) {
|
||||
Column() {
|
||||
Crossfade(targetState = feedState) { state ->
|
||||
when (state) {
|
||||
is FeedState.Empty -> {
|
||||
FeedEmpty {
|
||||
isRefreshing = true
|
||||
Column() {
|
||||
Crossfade(targetState = feedState) { state ->
|
||||
when (state) {
|
||||
is FeedState.Empty -> {
|
||||
FeedEmpty {
|
||||
isRefreshing = true
|
||||
}
|
||||
}
|
||||
is FeedState.FeedError -> {
|
||||
FeedError(state.errorMessage) {
|
||||
isRefreshing = true
|
||||
}
|
||||
}
|
||||
is FeedState.Loaded -> {
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(
|
||||
top = 10.dp,
|
||||
bottom = 10.dp
|
||||
),
|
||||
reverseLayout = true,
|
||||
state = listState
|
||||
) {
|
||||
var previousDate: String = ""
|
||||
itemsIndexed(state.feed.value, key = { index, item -> if (index == 0) index else item.idHex }) { index, item ->
|
||||
ChatroomMessageCompose(item, routeForLastRead, accountViewModel = accountViewModel, navController = navController)
|
||||
}
|
||||
}
|
||||
is FeedState.FeedError -> {
|
||||
FeedError(state.errorMessage) {
|
||||
isRefreshing = true
|
||||
}
|
||||
}
|
||||
is FeedState.Loaded -> {
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(
|
||||
top = 10.dp,
|
||||
bottom = 10.dp
|
||||
),
|
||||
reverseLayout = true,
|
||||
state = listState
|
||||
) {
|
||||
var previousDate: String = ""
|
||||
itemsIndexed(state.feed, key = { index, item -> if (index == 0) index else item.idHex }) { index, item ->
|
||||
ChatroomMessageCompose(item, routeForLastRead, accountViewModel = accountViewModel, navController = navController)
|
||||
}
|
||||
}
|
||||
}
|
||||
FeedState.Loading -> {
|
||||
LoadingFeed()
|
||||
}
|
||||
}
|
||||
FeedState.Loading -> {
|
||||
LoadingFeed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ fun ChatroomListFeedView(viewModel: FeedViewModel, accountViewModel: AccountView
|
||||
),
|
||||
state = listState
|
||||
) {
|
||||
itemsIndexed(state.feed, key = { index, item -> item.idHex }) { index, item ->
|
||||
itemsIndexed(state.feed.value, key = { index, item -> item.idHex }) { index, item ->
|
||||
ChatroomCompose(item, accountViewModel = accountViewModel, navController = navController)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
|
||||
|
||||
sealed class FeedState {
|
||||
object Loading : FeedState()
|
||||
class Loaded(val feed: List<Note>) : FeedState()
|
||||
class Loaded(val feed: MutableState<List<Note>>) : FeedState()
|
||||
object Empty : FeedState()
|
||||
class FeedError(val errorMessage: String) : FeedState()
|
||||
}
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.Button
|
||||
@ -40,8 +41,6 @@ fun FeedView(viewModel: FeedViewModel, accountViewModel: AccountViewModel, navCo
|
||||
var isRefreshing by remember { mutableStateOf(false) }
|
||||
val swipeRefreshState = rememberSwipeRefreshState(isRefreshing)
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LaunchedEffect(isRefreshing) {
|
||||
if (isRefreshing) {
|
||||
viewModel.hardRefresh()
|
||||
@ -49,12 +48,15 @@ fun FeedView(viewModel: FeedViewModel, accountViewModel: AccountViewModel, navCo
|
||||
}
|
||||
}
|
||||
|
||||
println("FeedView Refresh ${feedState}")
|
||||
|
||||
SwipeRefresh(
|
||||
state = swipeRefreshState,
|
||||
onRefresh = {
|
||||
isRefreshing = true
|
||||
},
|
||||
) {
|
||||
|
||||
Column() {
|
||||
Crossfade(targetState = feedState) { state ->
|
||||
when (state) {
|
||||
@ -69,22 +71,12 @@ fun FeedView(viewModel: FeedViewModel, accountViewModel: AccountViewModel, navCo
|
||||
}
|
||||
}
|
||||
is FeedState.Loaded -> {
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(
|
||||
top = 10.dp,
|
||||
bottom = 10.dp
|
||||
),
|
||||
state = listState
|
||||
) {
|
||||
itemsIndexed(state.feed, key = { _, item -> item.idHex }) { index, item ->
|
||||
NoteCompose(item,
|
||||
isInnerNote = false,
|
||||
routeForLastRead = routeForLastRead,
|
||||
accountViewModel = accountViewModel,
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
}
|
||||
FeedLoaded(
|
||||
state,
|
||||
routeForLastRead,
|
||||
accountViewModel,
|
||||
navController
|
||||
)
|
||||
}
|
||||
FeedState.Loading -> {
|
||||
LoadingFeed()
|
||||
@ -95,6 +87,34 @@ fun FeedView(viewModel: FeedViewModel, accountViewModel: AccountViewModel, navCo
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeedLoaded(
|
||||
state: FeedState.Loaded,
|
||||
routeForLastRead: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
navController: NavController
|
||||
) {
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(
|
||||
top = 10.dp,
|
||||
bottom = 10.dp
|
||||
),
|
||||
state = listState
|
||||
) {
|
||||
itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { index, item ->
|
||||
NoteCompose(
|
||||
item,
|
||||
isInnerNote = false,
|
||||
routeForLastRead = routeForLastRead,
|
||||
accountViewModel = accountViewModel,
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoadingFeed() {
|
||||
Column(
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
@ -97,10 +98,15 @@ abstract class FeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel()
|
||||
}
|
||||
|
||||
fun updateFeed(notes: List<Note>) {
|
||||
val currentState = feedContent.value
|
||||
|
||||
if (notes.isEmpty()) {
|
||||
_feedContent.update { FeedState.Empty }
|
||||
} else if (currentState is FeedState.Loaded) {
|
||||
// updates the current list
|
||||
currentState.feed.value = notes
|
||||
} else {
|
||||
_feedContent.update { FeedState.Loaded(notes) }
|
||||
_feedContent.update { FeedState.Loaded(mutableStateOf(notes)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,9 +88,9 @@ fun ThreadFeedView(noteId: String, viewModel: FeedViewModel, accountViewModel: A
|
||||
listState.animateScrollToItem(noteIdPositionInThread, 0)
|
||||
}
|
||||
|
||||
val notePosition = state.feed.filter { it.idHex == noteId}.firstOrNull()
|
||||
val notePosition = state.feed.value.filter { it.idHex == noteId}.firstOrNull()
|
||||
if (notePosition != null) {
|
||||
noteIdPositionInThread = state.feed.indexOf(notePosition)
|
||||
noteIdPositionInThread = state.feed.value.indexOf(notePosition)
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
@ -100,7 +100,7 @@ fun ThreadFeedView(noteId: String, viewModel: FeedViewModel, accountViewModel: A
|
||||
),
|
||||
state = listState
|
||||
) {
|
||||
itemsIndexed(state.feed, key = { _, item -> item.idHex }) { index, item ->
|
||||
itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { index, item ->
|
||||
if (index == 0)
|
||||
NoteMaster(item, accountViewModel = accountViewModel, navController = navController)
|
||||
else {
|
||||
|
@ -1,10 +1,11 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
|
||||
sealed class UserFeedState {
|
||||
object Loading : UserFeedState()
|
||||
class Loaded(val feed: List<User>) : UserFeedState()
|
||||
class Loaded(val feed: MutableState<List<User>>) : UserFeedState()
|
||||
object Empty : UserFeedState()
|
||||
class FeedError(val errorMessage: String) : UserFeedState()
|
||||
}
|
@ -4,6 +4,7 @@ import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -28,8 +29,6 @@ fun UserFeedView(viewModel: UserFeedViewModel, accountViewModel: AccountViewMode
|
||||
var isRefreshing by remember { mutableStateOf(false) }
|
||||
val swipeRefreshState = rememberSwipeRefreshState(isRefreshing)
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LaunchedEffect(isRefreshing) {
|
||||
if (isRefreshing) {
|
||||
viewModel.refresh()
|
||||
@ -57,17 +56,7 @@ fun UserFeedView(viewModel: UserFeedViewModel, accountViewModel: AccountViewMode
|
||||
}
|
||||
}
|
||||
is UserFeedState.Loaded -> {
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(
|
||||
top = 10.dp,
|
||||
bottom = 10.dp
|
||||
),
|
||||
state = listState
|
||||
) {
|
||||
itemsIndexed(state.feed, key = { _, item -> item.pubkeyHex }) { index, item ->
|
||||
UserCompose(item, accountViewModel = accountViewModel, navController = navController)
|
||||
}
|
||||
}
|
||||
FeedLoaded(state, accountViewModel, navController)
|
||||
}
|
||||
UserFeedState.Loading -> {
|
||||
LoadingFeed()
|
||||
@ -77,3 +66,24 @@ fun UserFeedView(viewModel: UserFeedViewModel, accountViewModel: AccountViewMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeedLoaded(
|
||||
state: UserFeedState.Loaded,
|
||||
accountViewModel: AccountViewModel,
|
||||
navController: NavController
|
||||
) {
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(
|
||||
top = 10.dp,
|
||||
bottom = 10.dp
|
||||
),
|
||||
state = listState
|
||||
) {
|
||||
itemsIndexed(state.feed.value, key = { _, item -> item.pubkeyHex }) { index, item ->
|
||||
UserCompose(item, accountViewModel = accountViewModel, navController = navController)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,12 @@ package com.vitorpamplona.amethyst.ui.screen
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.LocalCacheState
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.NostrDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrHiddenAccountsDataSource
|
||||
@ -60,10 +62,15 @@ open class UserFeedViewModel(val dataSource: NostrDataSource<User>): ViewModel()
|
||||
fun updateFeed(notes: List<User>) {
|
||||
val scope = CoroutineScope(Job() + Dispatchers.Main)
|
||||
scope.launch {
|
||||
val currentState = feedContent.value
|
||||
|
||||
if (notes.isEmpty()) {
|
||||
_feedContent.update { UserFeedState.Empty }
|
||||
} else if (currentState is UserFeedState.Loaded) {
|
||||
// updates the current list
|
||||
currentState.feed.value = notes
|
||||
} else {
|
||||
_feedContent.update { UserFeedState.Loaded(notes) }
|
||||
_feedContent.update { UserFeedState.Loaded(mutableStateOf(notes)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
@ -425,7 +426,7 @@ fun FollowButton(onClick: () -> Unit) {
|
||||
backgroundColor = MaterialTheme.colors.primary
|
||||
)
|
||||
) {
|
||||
Text(text = "Follow", color = Color.White)
|
||||
Text(text = "Follow", color = Color.White, textAlign = TextAlign.Center)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user