diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index fcc59ffc1..fa1742de8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -1588,7 +1588,7 @@ object LocalCache : ILocalCache { val isVerified = if (event.createdAt > oldChannel.updatedMetadataAt) { if (wasVerified || justVerify(event)) { - oldChannel.updateChannelInfo(author, event) + oldChannel.updateChannelInfo(author, event, note) true } else { false diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip28PublicChats/PublicChatChannel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip28PublicChats/PublicChatChannel.kt index cb77d53c8..5f1214f87 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip28PublicChats/PublicChatChannel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip28PublicChats/PublicChatChannel.kt @@ -40,7 +40,10 @@ class PublicChatChannel( ) : Channel() { var creator: User? = null var event: ChannelCreateEvent? = null - var eventNote: Note? = null + + // Important to keep this long-term reference because LocalCache uses WeakReferences. + var creationEventNote: Note? = null + var updateEventNote: Note? = null var info = ChannelDataNorm(null, null, null, null) var infoTags = EmptyTagList @@ -71,7 +74,7 @@ class PublicChatChannel( this.infoTags = event.tags.toImmutableListOfLists() this.updatedMetadataAt = event.createdAt - this.eventNote = eventNote + this.creationEventNote = eventNote updateChannelInfo() } @@ -79,12 +82,14 @@ class PublicChatChannel( fun updateChannelInfo( creator: User, event: ChannelMetadataEvent, + eventNote: Note? = null, ) { this.creator = creator this.info = event.channelInfo() this.infoTags = event.tags.toImmutableListOfLists() this.updatedMetadataAt = event.createdAt + this.updateEventNote = eventNote super.updateChannelInfo() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip53LiveActivities/LiveActivitiesChannel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip53LiveActivities/LiveActivitiesChannel.kt index 800ea64c5..d1ca69f69 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip53LiveActivities/LiveActivitiesChannel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip53LiveActivities/LiveActivitiesChannel.kt @@ -36,6 +36,8 @@ class LiveActivitiesChannel( ) : Channel() { var creator: User? = null var info: LiveActivitiesEvent? = null + + // Important to keep this long-term reference because LocalCache uses WeakReferences. var infoNote: Note? = null fun address() = address diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/RenderVideoPlayer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/RenderVideoPlayer.kt index bf5eb3450..4c87c7700 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/RenderVideoPlayer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/RenderVideoPlayer.kt @@ -67,8 +67,8 @@ fun RenderVideoPlayer( // if we alrady know the size of the frame, this forces the player to stay in the size layoutParams = FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT, ) setShowBuffering(PlayerView.SHOW_BUFFERING_ALWAYS) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/ChannelFeedContentState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/ChannelFeedContentState.kt index c49976e69..26d465a95 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/ChannelFeedContentState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/ChannelFeedContentState.kt @@ -24,8 +24,8 @@ import android.util.Log import androidx.compose.runtime.MutableState import androidx.compose.runtime.Stable import androidx.compose.runtime.mutableStateOf +import com.vitorpamplona.amethyst.model.Channel import com.vitorpamplona.amethyst.model.Note -import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatChannel import com.vitorpamplona.amethyst.service.checkNotInMainThread import com.vitorpamplona.amethyst.ui.dal.AdditiveComplexFeedFilter import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists @@ -41,7 +41,7 @@ import kotlinx.coroutines.launch @Stable class ChannelFeedContentState( - val localFilter: AdditiveComplexFeedFilter, + val localFilter: AdditiveComplexFeedFilter, val viewModelScope: CoroutineScope, ) : InvalidatableContent { private val _feedContent = MutableStateFlow(ChannelFeedState.Loading) @@ -92,15 +92,15 @@ class ChannelFeedContentState( } } - private fun updateFeed(notes: ImmutableList) { + private fun updateFeed(notes: ImmutableList) { val currentState = _feedContent.value if (notes.isEmpty()) { _feedContent.tryEmit(ChannelFeedState.Empty) } else if (currentState is ChannelFeedState.Loaded) { - currentState.feed.tryEmit(LoadedFeedState(notes, localFilter.showHiddenKey())) + currentState.feed.tryEmit(LoadedFeedState(notes, localFilter.showHiddenKey())) } else { _feedContent.tryEmit( - ChannelFeedState.Loaded(MutableStateFlow(LoadedFeedState(notes, localFilter.showHiddenKey()))), + ChannelFeedState.Loaded(MutableStateFlow(LoadedFeedState(notes, localFilter.showHiddenKey()))), ) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/ChannelFeedState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/ChannelFeedState.kt index 1c6fa3158..123a3e4d6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/ChannelFeedState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/ChannelFeedState.kt @@ -21,7 +21,7 @@ package com.vitorpamplona.amethyst.ui.feeds import androidx.compose.runtime.Stable -import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatChannel +import com.vitorpamplona.amethyst.model.Channel import kotlinx.coroutines.flow.MutableStateFlow @Stable @@ -29,7 +29,7 @@ sealed class ChannelFeedState { object Loading : ChannelFeedState() class Loaded( - val feed: MutableStateFlow>, + val feed: MutableStateFlow>, ) : ChannelFeedState() object Empty : ChannelFeedState() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/LiveActivityChatMessage.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/LiveActivityChatMessage.kt index 6526e77f4..fb8822831 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/LiveActivityChatMessage.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/LiveActivityChatMessage.kt @@ -34,7 +34,7 @@ import com.vitorpamplona.amethyst.ui.components.GenericLoadable import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.note.LoadLiveActivityChannel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.LiveActivitiesChannelHeader +import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.header.LiveActivitiesChannelHeader import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.ui.theme.replyModifier import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/ChannelView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/ChannelView.kt index 2e09c9d64..bb54a6f3f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/ChannelView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/ChannelView.kt @@ -26,7 +26,6 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel @@ -113,12 +112,10 @@ fun LiveActivityChannelView( Column(Modifier.fillMaxHeight()) { Column( modifier = - remember { - Modifier - .fillMaxHeight() - .padding(vertical = 0.dp) - .weight(1f, true) - }, + Modifier + .fillMaxHeight() + .padding(vertical = 0.dp) + .weight(1f, true), ) { ShowVideoStreaming(channel, accountViewModel) RefreshingChatroomFeedView( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/LiveActivityChannelScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/LiveActivityChannelScreen.kt index f078f7ece..03d052d8d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/LiveActivityChannelScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/LiveActivityChannelScreen.kt @@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53 import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.vitorpamplona.amethyst.model.Note @@ -51,7 +50,7 @@ fun LiveActivityChannelScreen( }, accountViewModel = accountViewModel, ) { - Column(Modifier.padding(it).statusBarsPadding()) { + Column(Modifier.padding(it)) { LiveActivityChannelView(channelId, draft, accountViewModel, nav) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/LiveActivitiesChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveActivitiesChannelHeader.kt similarity index 89% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/LiveActivitiesChannelHeader.kt rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveActivitiesChannelHeader.kt index 7005fcde5..b9fe94e32 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/LiveActivitiesChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveActivitiesChannelHeader.kt @@ -18,7 +18,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities +package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.header import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -32,6 +32,7 @@ import com.vitorpamplona.amethyst.model.nip53LiveActivities.LiveActivitiesChanne import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.ShowVideoStreaming import com.vitorpamplona.amethyst.ui.theme.StdPadding @Composable @@ -70,7 +71,11 @@ fun LiveActivitiesChannelHeader( ) if (expanded.value) { - LongLiveActivityChannelHeader(baseChannel, accountViewModel = accountViewModel, nav = nav) + LongLiveActivityChannelHeader( + baseChannel, + accountViewModel = accountViewModel, + nav = nav, + ) } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveActivityTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveActivityTopBar.kt index fa6e0516e..b2eeca605 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveActivityTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveActivityTopBar.kt @@ -25,8 +25,6 @@ import com.vitorpamplona.amethyst.model.nip53LiveActivities.LiveActivitiesChanne import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarExtensibleWithBackButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.LongLiveActivityChannelHeader -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.ShortLiveActivityChannelHeader @Composable fun LiveActivityTopBar( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/LongLiveActivityChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LongLiveActivityChannelHeader.kt similarity index 99% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/LongLiveActivityChannelHeader.kt rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LongLiveActivityChannelHeader.kt index 6f4d0918c..b6f1a24de 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/LongLiveActivityChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LongLiveActivityChannelHeader.kt @@ -18,7 +18,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities +package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.header import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/ShortLiveActivityChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/ShortLiveActivityChannelHeader.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/ShortLiveActivityChannelHeader.kt rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/ShortLiveActivityChannelHeader.kt index 0517c843c..7b61a30b6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/ShortLiveActivityChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/ShortLiveActivityChannelHeader.kt @@ -18,7 +18,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities +package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.header import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -45,6 +45,7 @@ import com.vitorpamplona.amethyst.ui.note.LikeReaction import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.note.ZapReaction import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.LiveFlag import com.vitorpamplona.amethyst.ui.theme.RowColSpacing import com.vitorpamplona.amethyst.ui.theme.Size34dp import com.vitorpamplona.amethyst.ui.theme.Size35dp diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip53LiveActivities/LiveActivityCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip53LiveActivities/LiveActivityCard.kt index b04f7b8f2..63a76c3c0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip53LiveActivities/LiveActivityCard.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip53LiveActivities/LiveActivityCard.kt @@ -60,10 +60,10 @@ import com.vitorpamplona.amethyst.ui.note.Gallery import com.vitorpamplona.amethyst.ui.note.LoadLiveActivityChannel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.EndedFlag -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.LiveActivitiesChannelHeader import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.LiveFlag import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.OfflineFlag import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.ScheduledFlag +import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.header.LiveActivitiesChannelHeader import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CheckIfVideoIsOnline import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/HomeScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/HomeScreen.kt index dab1e7660..9d2fd91eb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/HomeScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/HomeScreen.kt @@ -57,6 +57,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.AROUND_ME +import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatChannel +import com.vitorpamplona.amethyst.model.nip53LiveActivities.LiveActivitiesChannel import com.vitorpamplona.amethyst.service.OnlineChecker import com.vitorpamplona.amethyst.service.OnlineChecker.isOnline import com.vitorpamplona.amethyst.service.location.LocationState @@ -81,6 +83,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.geohash.NewGeoPostButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.HomeFilterAssemblerSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.live.RenderEphemeralBubble +import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.live.RenderLiveActivityBubble import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.amethyst.ui.theme.FeedPadding @@ -345,8 +348,11 @@ fun DisplayLiveBubbles( val feed by liveFeed.feed.collectAsStateWithLifecycle() LazyRow(HorzPadding, horizontalArrangement = spacedBy(Size5dp)) { - itemsIndexed(feed.list, key = { _, item -> item.roomId.toKey() }) { _, item -> - RenderEphemeralBubble(item, accountViewModel, nav) + itemsIndexed(feed.list, key = { _, item -> item.hashCode() }) { _, item -> + when (item) { + is EphemeralChatChannel -> RenderEphemeralBubble(item, accountViewModel, nav) + is LiveActivitiesChannel -> RenderLiveActivityBubble(item, accountViewModel, nav) + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeLiveFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeLiveFilter.kt index b7e819a57..eb3be9e65 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeLiveFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeLiveFilter.kt @@ -21,9 +21,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.home.dal import com.vitorpamplona.amethyst.model.Account +import com.vitorpamplona.amethyst.model.Channel import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatChannel +import com.vitorpamplona.amethyst.model.nip53LiveActivities.LiveActivitiesChannel import com.vitorpamplona.amethyst.model.topNavFeeds.allFollows.AllFollowsByOutboxTopNavFilter import com.vitorpamplona.amethyst.model.topNavFeeds.allFollows.AllFollowsByProxyTopNavFilter import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.author.AuthorsByOutboxTopNavFilter @@ -35,11 +37,13 @@ import com.vitorpamplona.amethyst.ui.dal.AdditiveComplexFeedFilter import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent +import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.StatusTag import com.vitorpamplona.quartz.utils.TimeUtils class HomeLiveFilter( val account: Account, -) : AdditiveComplexFeedFilter() { +) : AdditiveComplexFeedFilter() { override fun feedKey(): String = account.userProfile().pubkeyHex override fun showHiddenKey(): Boolean = false @@ -52,14 +56,17 @@ class HomeLiveFilter( fun limitTime() = TimeUtils.fifteenMinutesAgo() - override fun feed(): List { + override fun feed(): List { val filterParams = buildFilterParams(account) - val fiveMinsAgo = limitTime() + val fifteenMinsAgo = limitTime() val list = LocalCache.ephemeralChannels.filter { id, channel -> - shouldIncludeChannel(channel, filterParams, fiveMinsAgo) - } + shouldIncludeChannel(channel, filterParams, fifteenMinsAgo) + } + + LocalCache.liveChatChannels.filter { id, channel -> + shouldIncludeChannel(channel, filterParams, fifteenMinsAgo) + } return sort(list.toSet()) } @@ -71,18 +78,42 @@ class HomeLiveFilter( ): Boolean = channel.notes .filter { key, value -> - acceptableEvent(value, filterParams, timeLimit) + acceptableChatEvent(value, filterParams, timeLimit) }.isNotEmpty() + fun shouldIncludeChannel( + channel: LiveActivitiesChannel, + filterParams: FilterByListParams, + timeLimit: Long, + ): Boolean { + val liveChannel = + channel.info?.let { + it.createdAt > timeLimit && + it.status() == StatusTag.STATUS.LIVE && + filterParams.match(it, channel.relays().toList()) + } + + if (liveChannel == true) { + return true + } + + return channel.notes + .filter { key, value -> + acceptableChatEvent(value, filterParams, timeLimit) + }.isNotEmpty() + } + override fun updateListWith( - oldList: List, + oldList: List, newItems: Set, - ): List { - val fiveMinsAgo = limitTime() + ): List { + val fifteenMinsAgo = limitTime() val revisedOldList = oldList.filter { channel -> - (channel.lastNote?.createdAt() ?: 0) > fiveMinsAgo + val channelTime = (channel as? LiveActivitiesChannel)?.info?.createdAt + (channelTime == null || channelTime > fifteenMinsAgo) || + (channel.lastNote?.createdAt() ?: 0) > fifteenMinsAgo } val newItemsToBeAdded = applyFilter(newItems) @@ -94,7 +125,12 @@ class HomeLiveFilter( if (room != null) { LocalCache.getEphemeralChatChannelIfExists(room) } else { - null + val liveStream = (it.event as? LiveActivitiesChatMessageEvent)?.activityAddress() + if (liveStream != null) { + LocalCache.getLiveActivityChannelIfExists(liveStream) + } else { + null + } } } @@ -109,23 +145,23 @@ class HomeLiveFilter( val filterParams = buildFilterParams(account) return collection.filterTo(HashSet()) { - acceptableEvent(it, filterParams, limitTime()) + acceptableChatEvent(it, filterParams, limitTime()) } } - private fun acceptableEvent( + private fun acceptableChatEvent( note: Note, filterParams: FilterByListParams, timeLimit: Long, ): Boolean { val createdAt = note.createdAt() ?: return false val noteEvent = note.event - return (noteEvent is EphemeralChatEvent) && + return (noteEvent is EphemeralChatEvent || noteEvent is LiveActivitiesChatMessageEvent) && createdAt > timeLimit && filterParams.match(noteEvent, note.relays) } - fun sort(collection: Set): List { + fun sort(collection: Set): List { val topFilter = account.liveHomeFollowLists.value val topFilterAuthors = when (topFilter) { @@ -145,15 +181,14 @@ class HomeLiveFilter( collection.associateWith { followsThatParticipateOn(it, followingKeySet) } return collection.sortedWith( - compareByDescending { followCounts[it] } - .thenByDescending { it.lastNote?.createdAt() ?: 0 } - .thenBy { it.roomId.id } - .thenBy { it.roomId.relayUrl }, + compareByDescending { followCounts[it] } + .thenByDescending { it.lastNote?.createdAt() ?: 0 } + .thenBy { it.hashCode() }, ) } fun followsThatParticipateOn( - channel: EphemeralChatChannel, + channel: Channel, followingSet: Set?, ): Int { var count = 0 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/live/RenderLiveActivityBubble.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/live/RenderLiveActivityBubble.kt new file mode 100644 index 000000000..b2f0b7bda --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/live/RenderLiveActivityBubble.kt @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.ui.screen.loggedIn.home.live + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.vitorpamplona.amethyst.model.nip53LiveActivities.LiveActivitiesChannel +import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannelNoteAuthors +import com.vitorpamplona.amethyst.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor +import com.vitorpamplona.amethyst.ui.note.Gallery +import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer + +@Composable +fun RenderLiveActivityBubble( + channel: LiveActivitiesChannel, + accountViewModel: AccountViewModel, + nav: INav, +) { + FilledTonalButton( + contentPadding = PaddingValues(start = 8.dp, end = 10.dp, bottom = 0.dp, top = 0.dp), + onClick = { + nav.nav { routeFor(channel) } + }, + ) { + RenderUsers(channel, accountViewModel, nav) + Spacer(StdHorzSpacer) + Text( + channel.toBestDisplayName(), + ) + } +} + +@Composable +fun RenderUsers( + channel: LiveActivitiesChannel, + accountViewModel: AccountViewModel, + nav: INav, +) { + val authors by observeChannelNoteAuthors(channel, accountViewModel) + + Gallery(authors, Modifier, accountViewModel, nav, 3) +}