Merge pull request #2069 from vitorpamplona/claude/nip-53-compliance-mh5pD
Add support for NIP-53 Meeting Spaces and Meeting Rooms
This commit is contained in:
@@ -163,6 +163,9 @@ import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.rsvp.CalendarRSVPEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.presence.MeetingRoomPresenceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportEvent
|
||||
@@ -2623,6 +2626,9 @@ object LocalCache : ILocalCache, ICacheProvider {
|
||||
is LabeledBookmarkListEvent -> consumeBaseReplaceable(event, relay, wasVerified)
|
||||
is LiveActivitiesEvent -> consume(event, relay, wasVerified)
|
||||
is LiveActivitiesChatMessageEvent -> consume(event, relay, wasVerified)
|
||||
is MeetingSpaceEvent -> consumeBaseReplaceable(event, relay, wasVerified)
|
||||
is MeetingRoomEvent -> consumeBaseReplaceable(event, relay, wasVerified)
|
||||
is MeetingRoomPresenceEvent -> consumeBaseReplaceable(event, relay, wasVerified)
|
||||
is LnZapEvent -> consume(event, relay, wasVerified)
|
||||
is LnZapRequestEvent -> consume(event, relay, wasVerified)
|
||||
is NIP90StatusEvent -> consumeRegularEvent(event, relay, wasVerified)
|
||||
|
||||
@@ -138,6 +138,8 @@ import com.vitorpamplona.amethyst.ui.note.types.RenderLiveChessChallenge
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderLiveChessGameEnd
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderLnZap
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderLongFormContent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderMeetingRoomEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderMeetingSpaceEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderMintRecommendation
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderNIP90ContentDiscoveryResponse
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderNIP90Status
|
||||
@@ -251,6 +253,8 @@ import com.vitorpamplona.quartz.nip51Lists.relaySets.RelaySetEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportEvent
|
||||
@@ -987,6 +991,14 @@ private fun RenderNoteRow(
|
||||
RenderLiveActivityEvent(baseNote, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is MeetingSpaceEvent -> {
|
||||
RenderMeetingSpaceEvent(baseNote, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is MeetingRoomEvent -> {
|
||||
RenderMeetingRoomEvent(baseNote, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is GitRepositoryEvent -> {
|
||||
RenderGitRepositoryEvent(baseNote, accountViewModel, nav)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* 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.note.types
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.LiveFlag
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.ScheduledFlag
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CrossfadeCheckIfVideoIsOnline
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.SmallBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.ParticipantTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.StatusTag
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.util.Locale
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.StatusTag as MeetingSpaceStatusTag
|
||||
|
||||
@Composable
|
||||
fun RenderMeetingSpaceEvent(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
Row(modifier = Modifier.padding(top = 5.dp)) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
RenderMeetingSpaceEventInner(baseNote = baseNote, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderMeetingSpaceEventInner(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = baseNote.event as? MeetingSpaceEvent ?: return
|
||||
|
||||
val eventUpdates by observeNote(baseNote, accountViewModel)
|
||||
|
||||
val roomName = remember(eventUpdates) { noteEvent.room() }
|
||||
val summary = remember(eventUpdates) { noteEvent.summary() }
|
||||
val status = remember(eventUpdates) { noteEvent.status() }
|
||||
val participants = remember(eventUpdates) { noteEvent.participants() }
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
roomName?.let {
|
||||
Text(
|
||||
text = it,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
|
||||
CrossfadeIfEnabled(targetState = status, label = "MeetingSpaceStatus", accountViewModel = accountViewModel) {
|
||||
when (it) {
|
||||
MeetingSpaceStatusTag.STATUS.OPEN -> {
|
||||
MeetingSpaceOpenFlag()
|
||||
}
|
||||
|
||||
MeetingSpaceStatusTag.STATUS.PRIVATE -> {
|
||||
MeetingSpacePrivateFlag()
|
||||
}
|
||||
|
||||
MeetingSpaceStatusTag.STATUS.CLOSED -> {
|
||||
MeetingSpaceClosedFlag()
|
||||
}
|
||||
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
summary?.let {
|
||||
Text(
|
||||
text = it,
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 5.dp),
|
||||
)
|
||||
}
|
||||
|
||||
RenderParticipants(participants, accountViewModel, nav)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderMeetingRoomEvent(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
Row(modifier = Modifier.padding(top = 5.dp)) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
RenderMeetingRoomEventInner(baseNote = baseNote, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderMeetingRoomEventInner(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = baseNote.event as? MeetingRoomEvent ?: return
|
||||
|
||||
val eventUpdates by observeNote(baseNote, accountViewModel)
|
||||
|
||||
val subject = remember(eventUpdates) { noteEvent.title() }
|
||||
val media = remember(eventUpdates) { noteEvent.streaming() }
|
||||
val status = remember(eventUpdates) { noteEvent.status() }
|
||||
val starts = remember(eventUpdates) { noteEvent.starts() }
|
||||
val participants = remember(eventUpdates) { noteEvent.participants() }
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
subject?.let {
|
||||
Text(
|
||||
text = it,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
|
||||
CrossfadeIfEnabled(targetState = status, label = "MeetingRoomStatus", accountViewModel = accountViewModel) {
|
||||
when (it) {
|
||||
StatusTag.STATUS.LIVE -> {
|
||||
media?.let {
|
||||
CrossfadeCheckIfVideoIsOnline(it, accountViewModel) {
|
||||
LiveFlag()
|
||||
}
|
||||
} ?: LiveFlag()
|
||||
}
|
||||
|
||||
StatusTag.STATUS.PLANNED -> {
|
||||
ScheduledFlag(starts)
|
||||
}
|
||||
|
||||
StatusTag.STATUS.ENDED -> {}
|
||||
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderParticipants(participants, accountViewModel, nav)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderParticipants(
|
||||
participants: List<ParticipantTag>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
var participantUsers by remember {
|
||||
mutableStateOf<ImmutableList<Pair<ParticipantTag, User>>>(
|
||||
persistentListOf(),
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = participants) {
|
||||
accountViewModel.loadParticipants(participants) { newParticipantUsers ->
|
||||
if (!equalImmutableLists(newParticipantUsers, participantUsers)) {
|
||||
participantUsers = newParticipantUsers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
participantUsers.forEach {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
.clickable { nav.nav(routeFor(it.second)) },
|
||||
) {
|
||||
ClickableUserPicture(it.second, 25.dp, accountViewModel)
|
||||
Spacer(StdHorzSpacer)
|
||||
UsernameDisplay(it.second, Modifier.weight(1f), accountViewModel = accountViewModel)
|
||||
Spacer(StdHorzSpacer)
|
||||
it.first.role?.let {
|
||||
Text(
|
||||
text =
|
||||
it.replaceFirstChar {
|
||||
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
|
||||
},
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MeetingSpaceOpenFlag() {
|
||||
Text(
|
||||
text = stringRes(id = R.string.meeting_space_open_tag),
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 16.sp,
|
||||
modifier =
|
||||
remember {
|
||||
Modifier
|
||||
.clip(SmallBorder)
|
||||
.background(Color(0xFF4CAF50))
|
||||
.padding(horizontal = 5.dp)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MeetingSpacePrivateFlag() {
|
||||
Text(
|
||||
text = stringRes(id = R.string.meeting_space_private_tag),
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 16.sp,
|
||||
modifier =
|
||||
remember {
|
||||
Modifier
|
||||
.clip(SmallBorder)
|
||||
.background(Color(0xFFFF9800))
|
||||
.padding(horizontal = 5.dp)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MeetingSpaceClosedFlag() {
|
||||
Text(
|
||||
text = stringRes(id = R.string.meeting_space_closed_tag),
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 16.sp,
|
||||
modifier =
|
||||
remember {
|
||||
Modifier
|
||||
.clip(SmallBorder)
|
||||
.background(Color.Black)
|
||||
.padding(horizontal = 5.dp)
|
||||
},
|
||||
)
|
||||
}
|
||||
+6
@@ -46,6 +46,8 @@ import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
||||
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||
@@ -149,6 +151,8 @@ fun InnerChannelCardWithReactions(
|
||||
) {
|
||||
when (baseNote.event) {
|
||||
is LiveActivitiesEvent -> InnerCardRow(baseNote, accountViewModel, nav)
|
||||
is MeetingSpaceEvent -> InnerCardRow(baseNote, accountViewModel, nav)
|
||||
is MeetingRoomEvent -> InnerCardRow(baseNote, accountViewModel, nav)
|
||||
is CommunityDefinitionEvent -> InnerCardRow(baseNote, accountViewModel, nav)
|
||||
is ChannelCreateEvent -> InnerCardRow(baseNote, accountViewModel, nav)
|
||||
is ClassifiedsEvent -> InnerCardBox(baseNote, accountViewModel, nav)
|
||||
@@ -202,6 +206,8 @@ private fun RenderNoteRow(
|
||||
) {
|
||||
when (baseNote.event) {
|
||||
is LiveActivitiesEvent -> RenderLiveActivityThumb(baseNote, accountViewModel, nav)
|
||||
is MeetingSpaceEvent -> RenderLiveActivityThumb(baseNote, accountViewModel, nav)
|
||||
is MeetingRoomEvent -> RenderLiveActivityThumb(baseNote, accountViewModel, nav)
|
||||
is CommunityDefinitionEvent -> RenderCommunitiesThumb(baseNote, accountViewModel, nav)
|
||||
is ChannelCreateEvent -> RenderPublicChatChannelThumb(baseNote, accountViewModel, nav)
|
||||
is AppDefinitionEvent -> RenderContentDVMThumb(baseNote, accountViewModel, nav)
|
||||
|
||||
+46
-15
@@ -35,6 +35,8 @@ import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.muted.MutedAuthors
|
||||
import com.vitorpamplona.amethyst.service.OnlineChecker
|
||||
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.StatusTag
|
||||
|
||||
@@ -75,7 +77,8 @@ open class DiscoverLiveFeedFilter(
|
||||
|
||||
return collection.filterTo(HashSet()) {
|
||||
val noteEvent = it.event
|
||||
noteEvent is LiveActivitiesEvent && filterParams.match(noteEvent, it.relays)
|
||||
(noteEvent is LiveActivitiesEvent || noteEvent is MeetingSpaceEvent || noteEvent is MeetingRoomEvent) &&
|
||||
filterParams.match(noteEvent, it.relays)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,33 +109,61 @@ open class DiscoverLiveFeedFilter(
|
||||
return items
|
||||
.sortedWith(
|
||||
compareBy(
|
||||
{ convertStatusToOrder((it.event as? LiveActivitiesEvent)) },
|
||||
{ convertStatusToOrder(it.event) },
|
||||
{ participantCounts[it] },
|
||||
{ allParticipants[it] },
|
||||
{ (it.event as? LiveActivitiesEvent)?.starts() ?: it.createdAt() },
|
||||
{
|
||||
when (val e = it.event) {
|
||||
is LiveActivitiesEvent -> e.starts() ?: it.createdAt()
|
||||
is MeetingRoomEvent -> e.starts() ?: it.createdAt()
|
||||
else -> it.createdAt()
|
||||
}
|
||||
},
|
||||
{ it.idHex },
|
||||
),
|
||||
).reversed()
|
||||
}
|
||||
|
||||
fun convertStatusToOrder(event: LiveActivitiesEvent?): Int {
|
||||
fun convertStatusToOrder(event: com.vitorpamplona.quartz.nip01Core.core.Event?): Int {
|
||||
if (event == null) return 0
|
||||
val url = event.streaming() ?: return 0
|
||||
return when (event.status()) {
|
||||
StatusTag.STATUS.LIVE -> {
|
||||
if (OnlineChecker.isCachedAndOffline(url)) {
|
||||
0
|
||||
} else {
|
||||
2
|
||||
return when (event) {
|
||||
is LiveActivitiesEvent -> {
|
||||
val url = event.streaming() ?: return 0
|
||||
when (event.status()) {
|
||||
StatusTag.STATUS.LIVE -> {
|
||||
if (OnlineChecker.isCachedAndOffline(url)) 0 else 2
|
||||
}
|
||||
|
||||
StatusTag.STATUS.PLANNED -> {
|
||||
1
|
||||
}
|
||||
|
||||
StatusTag.STATUS.ENDED -> {
|
||||
0
|
||||
}
|
||||
|
||||
else -> {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusTag.STATUS.PLANNED -> {
|
||||
1
|
||||
is MeetingRoomEvent -> {
|
||||
when (event.status()) {
|
||||
StatusTag.STATUS.LIVE -> 2
|
||||
StatusTag.STATUS.PLANNED -> 1
|
||||
StatusTag.STATUS.ENDED -> 0
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
StatusTag.STATUS.ENDED -> {
|
||||
0
|
||||
is MeetingSpaceEvent -> {
|
||||
when (event.status()) {
|
||||
com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.StatusTag.STATUS.OPEN -> 2
|
||||
com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.StatusTag.STATUS.PRIVATE -> 1
|
||||
com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.StatusTag.STATUS.CLOSED -> 0
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
+64
-12
@@ -69,6 +69,8 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutabl
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.ParticipantTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.StatusTag
|
||||
@@ -98,19 +100,69 @@ fun RenderLiveActivityThumb(
|
||||
nav: INav,
|
||||
) {
|
||||
val card by observeNoteAndMap(baseNote, accountViewModel) {
|
||||
val noteEvent = it.event as? LiveActivitiesEvent
|
||||
when (val noteEvent = it.event) {
|
||||
is LiveActivitiesEvent -> {
|
||||
LiveActivityCard(
|
||||
id = noteEvent.address(),
|
||||
name = noteEvent.dTag(),
|
||||
cover = noteEvent.image()?.ifBlank { null },
|
||||
media = noteEvent.streaming(),
|
||||
subject = noteEvent.title()?.ifBlank { null },
|
||||
content = noteEvent.summary(),
|
||||
participants = noteEvent.participants().toImmutableList(),
|
||||
status = noteEvent.status(),
|
||||
starts = noteEvent.starts(),
|
||||
)
|
||||
}
|
||||
|
||||
LiveActivityCard(
|
||||
id = noteEvent?.address(),
|
||||
name = noteEvent?.dTag() ?: "",
|
||||
cover = noteEvent?.image()?.ifBlank { null },
|
||||
media = noteEvent?.streaming(),
|
||||
subject = noteEvent?.title()?.ifBlank { null },
|
||||
content = noteEvent?.summary(),
|
||||
participants = noteEvent?.participants()?.toImmutableList() ?: persistentListOf(),
|
||||
status = noteEvent?.status(),
|
||||
starts = noteEvent?.starts(),
|
||||
)
|
||||
is MeetingRoomEvent -> {
|
||||
LiveActivityCard(
|
||||
id = noteEvent.address(),
|
||||
name = noteEvent.dTag(),
|
||||
cover = noteEvent.image()?.ifBlank { null },
|
||||
media = noteEvent.streaming(),
|
||||
subject = noteEvent.title()?.ifBlank { null },
|
||||
content = noteEvent.summary(),
|
||||
participants = noteEvent.participants().toImmutableList(),
|
||||
status = noteEvent.status(),
|
||||
starts = noteEvent.starts(),
|
||||
)
|
||||
}
|
||||
|
||||
is MeetingSpaceEvent -> {
|
||||
LiveActivityCard(
|
||||
id = noteEvent.address(),
|
||||
name = noteEvent.dTag(),
|
||||
cover = noteEvent.image()?.ifBlank { null },
|
||||
media = null,
|
||||
subject = noteEvent.room()?.ifBlank { null },
|
||||
content = noteEvent.summary(),
|
||||
participants = noteEvent.participants().toImmutableList(),
|
||||
status =
|
||||
when (noteEvent.status()) {
|
||||
com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.StatusTag.STATUS.OPEN -> StatusTag.STATUS.LIVE
|
||||
com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.StatusTag.STATUS.PRIVATE -> StatusTag.STATUS.PLANNED
|
||||
com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.StatusTag.STATUS.CLOSED -> StatusTag.STATUS.ENDED
|
||||
else -> null
|
||||
},
|
||||
starts = null,
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
LiveActivityCard(
|
||||
id = null,
|
||||
name = "",
|
||||
cover = null,
|
||||
media = null,
|
||||
subject = null,
|
||||
content = null,
|
||||
participants = persistentListOf(),
|
||||
status = null,
|
||||
starts = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderLiveActivityThumb(
|
||||
|
||||
+4
-2
@@ -27,6 +27,8 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.approval.CommunityPostApprovalEvent
|
||||
|
||||
@@ -47,7 +49,7 @@ fun filterLiveActivitiesAllCommunities(
|
||||
tags =
|
||||
mapOf(
|
||||
"a" to communityList,
|
||||
"k" to listOf(LiveActivitiesChatMessageEvent.KIND.toString(), LiveActivitiesEvent.KIND.toString()),
|
||||
"k" to listOf(LiveActivitiesChatMessageEvent.KIND.toString(), LiveActivitiesEvent.KIND.toString(), MeetingSpaceEvent.KIND.toString(), MeetingRoomEvent.KIND.toString()),
|
||||
),
|
||||
limit = 300,
|
||||
since = since,
|
||||
@@ -59,7 +61,7 @@ fun filterLiveActivitiesAllCommunities(
|
||||
filter =
|
||||
Filter(
|
||||
tags = mapOf("k" to listOf("5300"), "a" to communityList),
|
||||
kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND),
|
||||
kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND, MeetingSpaceEvent.KIND, MeetingRoomEvent.KIND),
|
||||
limit = 300,
|
||||
since = since,
|
||||
),
|
||||
|
||||
+4
-2
@@ -28,6 +28,8 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
|
||||
fun filterLiveActivitiesAuthors(
|
||||
@@ -42,7 +44,7 @@ fun filterLiveActivitiesAuthors(
|
||||
filter =
|
||||
Filter(
|
||||
authors = authorList,
|
||||
kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND),
|
||||
kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND, MeetingSpaceEvent.KIND, MeetingRoomEvent.KIND),
|
||||
limit = 300,
|
||||
since = since,
|
||||
),
|
||||
@@ -53,7 +55,7 @@ fun filterLiveActivitiesAuthors(
|
||||
filter =
|
||||
Filter(
|
||||
tags = mapOf("p" to authorList),
|
||||
kinds = listOf(LiveActivitiesEvent.KIND),
|
||||
kinds = listOf(LiveActivitiesEvent.KIND, MeetingSpaceEvent.KIND, MeetingRoomEvent.KIND),
|
||||
limit = 100,
|
||||
since = since,
|
||||
),
|
||||
|
||||
+4
-2
@@ -26,6 +26,8 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.approval.CommunityPostApprovalEvent
|
||||
|
||||
@@ -47,7 +49,7 @@ fun filterLiveActivitiesByCommunity(
|
||||
tags =
|
||||
mapOf(
|
||||
"a" to listOf(community),
|
||||
"k" to listOf(LiveActivitiesChatMessageEvent.KIND.toString(), LiveActivitiesEvent.KIND.toString()),
|
||||
"k" to listOf(LiveActivitiesChatMessageEvent.KIND.toString(), LiveActivitiesEvent.KIND.toString(), MeetingSpaceEvent.KIND.toString(), MeetingRoomEvent.KIND.toString()),
|
||||
),
|
||||
limit = 300,
|
||||
since = since,
|
||||
@@ -60,7 +62,7 @@ fun filterLiveActivitiesByCommunity(
|
||||
Filter(
|
||||
authors = authors,
|
||||
tags = mapOf("k" to listOf("5300"), "a" to listOf(community)),
|
||||
kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND),
|
||||
kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND, MeetingSpaceEvent.KIND, MeetingRoomEvent.KIND),
|
||||
limit = 300,
|
||||
since = since,
|
||||
),
|
||||
|
||||
+3
-1
@@ -26,6 +26,8 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
|
||||
fun filterLiveActivitiesByGeohash(
|
||||
@@ -42,7 +44,7 @@ fun filterLiveActivitiesByGeohash(
|
||||
relay = relay,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND),
|
||||
kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND, MeetingSpaceEvent.KIND, MeetingRoomEvent.KIND),
|
||||
tags = mapOf("g" to geoHashes),
|
||||
limit = 300,
|
||||
since = since,
|
||||
|
||||
+3
-1
@@ -27,6 +27,8 @@ import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtagAlts
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
|
||||
fun filterLiveActivitiesByHashtag(
|
||||
@@ -43,7 +45,7 @@ fun filterLiveActivitiesByHashtag(
|
||||
relay = relay,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND),
|
||||
kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND, MeetingSpaceEvent.KIND, MeetingRoomEvent.KIND),
|
||||
tags = mapOf("t" to hashtags),
|
||||
limit = 300,
|
||||
since = since,
|
||||
|
||||
+3
-1
@@ -25,6 +25,8 @@ import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@@ -43,7 +45,7 @@ fun filterLiveActivitiesGlobal(
|
||||
relay = it.key,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = listOf(LiveActivitiesEvent.KIND),
|
||||
kinds = listOf(LiveActivitiesEvent.KIND, MeetingSpaceEvent.KIND, MeetingRoomEvent.KIND),
|
||||
limit = 30,
|
||||
since = since ?: TimeUtils.oneWeekAgo(),
|
||||
),
|
||||
|
||||
@@ -919,6 +919,9 @@
|
||||
|
||||
<string name="live_stream_is_offline">Livestream is Offline</string>
|
||||
<string name="live_stream_has_ended">Livestream Ended</string>
|
||||
<string name="meeting_space_open_tag">OPEN</string>
|
||||
<string name="meeting_space_private_tag">PRIVATE</string>
|
||||
<string name="meeting_space_closed_tag">CLOSED</string>
|
||||
<string name="are_you_sure_you_want_to_log_out">Logging out deletes all your local information. Make sure to have your private keys backed up to avoid losing your account. Do you want to continue?</string>
|
||||
<string name="followed_tags">Followed Tags</string>
|
||||
|
||||
|
||||
+3
-1
@@ -26,6 +26,8 @@ import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
|
||||
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
|
||||
@@ -56,7 +58,7 @@ object KindRegistry {
|
||||
"repost" to listOf(RepostEvent.KIND),
|
||||
"profile" to listOf(MetadataEvent.KIND),
|
||||
"channel" to listOf(ChannelCreateEvent.KIND, ChannelMetadataEvent.KIND),
|
||||
"live" to listOf(LiveActivitiesEvent.KIND),
|
||||
"live" to listOf(LiveActivitiesEvent.KIND, MeetingSpaceEvent.KIND, MeetingRoomEvent.KIND),
|
||||
"community" to listOf(CommunityDefinitionEvent.KIND),
|
||||
"wiki" to listOf(WikiNoteEvent.KIND),
|
||||
"classified" to listOf(ClassifiedsEvent.KIND),
|
||||
|
||||
+3
-2
@@ -37,6 +37,7 @@ enum class ROLE(
|
||||
HOST("host"),
|
||||
MODERATOR("moderator"),
|
||||
SPEAKER("speaker"),
|
||||
PARTICIPANT("participant"),
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@@ -58,7 +59,7 @@ data class ParticipantTag(
|
||||
ensure(tag.has(3)) { return false }
|
||||
ensure(tag[0] == TAG_NAME) { return false }
|
||||
ensure(tag[1].length == 64) { return false }
|
||||
ensure(tag[3] == ROLE.HOST.code) { return false }
|
||||
ensure(tag[3].equals(ROLE.HOST.code, ignoreCase = true)) { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -76,7 +77,7 @@ data class ParticipantTag(
|
||||
ensure(tag.has(3)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
ensure(tag[3] == ROLE.HOST.code) { return null }
|
||||
ensure(tag[3].equals(ROLE.HOST.code, ignoreCase = true)) { return null }
|
||||
|
||||
val hint = RelayUrlNormalizer.normalizeOrNull(tag[2])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user