diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt index 72330422c..e1f298a19 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt @@ -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): Card() { sealed class CardFeedState { object Loading: CardFeedState() - class Loaded(val feed: List): CardFeedState() + class Loaded(val feed: MutableState>): CardFeedState() object Empty: CardFeedState() class FeedError(val errorMessage: String): CardFeedState() } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt index 532dacb74..75df0a0de 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt @@ -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 + ) + } + } + } +} diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt index b3c39a08a..591643a16 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt @@ -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): 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): ViewModel() { fun updateFeed(notes: List) { 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) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomFeedView.kt index e73ba0bd1..f538dd0f3 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomFeedView.kt @@ -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() } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomListFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomListFeedView.kt index d829e49aa..7215ab3ab 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomListFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomListFeedView.kt @@ -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) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedState.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedState.kt index fe3361e79..4f058ac27 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedState.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedState.kt @@ -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) : FeedState() + class Loaded(val feed: MutableState>) : FeedState() object Empty : FeedState() class FeedError(val errorMessage: String) : FeedState() } \ No newline at end of file diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt index d582ec28b..7aea101ec 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt @@ -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( diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt index f12bc2805..d96b35db4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt @@ -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): ViewModel() } fun updateFeed(notes: List) { + 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)) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt index c0e47c3e2..ac5a74bb3 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt @@ -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 { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedState.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedState.kt index 61e24c462..f5cd706e0 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedState.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedState.kt @@ -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) : UserFeedState() + class Loaded(val feed: MutableState>) : UserFeedState() object Empty : UserFeedState() class FeedError(val errorMessage: String) : UserFeedState() } \ No newline at end of file diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedView.kt index 432442c0f..b75f649d4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedView.kt @@ -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) + } + } +} diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt index bb7016d2b..ad85ee181 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt @@ -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): ViewModel() fun updateFeed(notes: List) { 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)) } } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt index fe2b4b01d..fdbf95352 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt @@ -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) } }