diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt index 1672056e3..fca593fd7 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt @@ -241,6 +241,7 @@ fun ImageVideoPost(postViewModel: NewMediaModel, accountViewModel: AccountViewMo postViewModel.galleryUri?.let { VideoView( videoUri = it.toString(), + roundedCorner = false, accountViewModel = accountViewModel ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt index d1e4bf37f..3fdd34c84 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt @@ -385,7 +385,7 @@ fun NewPostView( ) ) } else if (videoExtensions.any { removedParamsFromUrl.endsWith(it) }) { - VideoView(myUrlPreview, accountViewModel = accountViewModel) + VideoView(myUrlPreview, roundedCorner = true, accountViewModel = accountViewModel) } else { UrlPreview(myUrlPreview, myUrlPreview, accountViewModel) } @@ -1365,7 +1365,7 @@ fun ImageVideoDescription( ) } } else { - VideoView(uri.toString(), accountViewModel = accountViewModel) + VideoView(uri.toString(), roundedCorner = true, accountViewModel = accountViewModel) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt index 70c5944e9..95450735e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt @@ -287,7 +287,7 @@ private fun ZoomableContentView( ) { state.imagesForPager[word]?.let { Box(modifier = HalfVertPadding) { - ZoomableContentView(it, state.imageList, accountViewModel) + ZoomableContentView(it, state.imageList, roundedCorner = true, accountViewModel) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt index 5de666a67..f75d6e69e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt @@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme @@ -71,6 +72,7 @@ import com.vitorpamplona.amethyst.ui.theme.PinBottomIconSize import com.vitorpamplona.amethyst.ui.theme.Size22Modifier import com.vitorpamplona.amethyst.ui.theme.Size50Modifier import com.vitorpamplona.amethyst.ui.theme.VolumeBottomIconSize +import com.vitorpamplona.amethyst.ui.theme.imageModifier import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -88,6 +90,7 @@ fun LoadThumbAndThenVideoView( title: String? = null, thumbUri: String, authorName: String? = null, + roundedCorner: Boolean, nostrUriCallback: String? = null, accountViewModel: AccountViewModel, onDialog: ((Boolean) -> Unit)? = null @@ -119,6 +122,7 @@ fun LoadThumbAndThenVideoView( videoUri = videoUri, title = title, thumb = VideoThumb(loadingFinished.second), + roundedCorner = roundedCorner, artworkUri = thumbUri, authorName = authorName, nostrUriCallback = nostrUriCallback, @@ -130,6 +134,7 @@ fun LoadThumbAndThenVideoView( videoUri = videoUri, title = title, thumb = null, + roundedCorner = roundedCorner, artworkUri = thumbUri, authorName = authorName, nostrUriCallback = nostrUriCallback, @@ -145,6 +150,7 @@ fun VideoView( videoUri: String, title: String? = null, thumb: VideoThumb? = null, + roundedCorner: Boolean, waveform: ImmutableList? = null, artworkUri: String? = null, authorName: String? = null, @@ -160,6 +166,7 @@ fun VideoView( defaultToStart, title, thumb, + roundedCorner, waveform, artworkUri, authorName, @@ -177,6 +184,7 @@ fun VideoViewInner( defaultToStart: Boolean = false, title: String? = null, thumb: VideoThumb? = null, + roundedCorner: Boolean, waveform: ImmutableList? = null, artworkUri: String? = null, authorName: String? = null, @@ -231,7 +239,16 @@ fun VideoViewInner( defaultToStart = defaultToStart, nostrUriCallback = nostrUriCallback ) { controller, keepPlaying -> - RenderVideoPlayer(controller, thumb, waveform, keepPlaying, automaticallyStartPlayback, activeOnScreen, onDialog) + RenderVideoPlayer( + controller = controller, + thumbData = thumb, + roundedCorner = roundedCorner, + waveform = waveform, + keepPlaying = keepPlaying, + automaticallyStartPlayback = automaticallyStartPlayback, + activeOnScreen = activeOnScreen, + onDialog = onDialog + ) } } } @@ -483,6 +500,7 @@ data class VideoThumb( private fun RenderVideoPlayer( controller: MediaController, thumbData: VideoThumb?, + roundedCorner: Boolean, waveform: ImmutableList? = null, keepPlaying: MutableState, automaticallyStartPlayback: MutableState, @@ -499,10 +517,15 @@ private fun RenderVideoPlayer( BoxWithConstraints() { AndroidView( - modifier = Modifier - .fillMaxWidth() - .defaultMinSize(minHeight = 100.dp) - .align(Alignment.Center), + modifier = if (roundedCorner) { + MaterialTheme.colors.imageModifier + .defaultMinSize(minHeight = 100.dp) + .align(Alignment.Center) + } else { + Modifier.fillMaxWidth() + .defaultMinSize(minHeight = 100.dp) + .align(Alignment.Center) + }, factory = { PlayerView(context).apply { player = controller @@ -620,7 +643,7 @@ fun Waveform( @Composable fun DrawWaveform(waveform: ImmutableList, waveformProgress: MutableState, align: Modifier) { AudioWaveformReadOnly( - modifier = align, + modifier = align.padding(start = 10.dp, end = 10.dp), amplitudes = waveform, progress = waveformProgress.value, progressBrush = Brush.infiniteLinearGradient( diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt index 1e0616cde..f2401812b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt @@ -181,6 +181,7 @@ fun figureOutMimeType(fullUrl: String): ZoomableContent { fun ZoomableContentView( content: ZoomableContent, images: ImmutableList = listOf(content).toImmutableList(), + roundedCorner: Boolean, accountViewModel: AccountViewModel ) { val clipboardManager = LocalClipboardManager.current @@ -190,7 +191,11 @@ fun ZoomableContentView( mutableStateOf(false) } - var mainImageModifier = MaterialTheme.colors.imageModifier + var mainImageModifier = if (roundedCorner) { + MaterialTheme.colors.imageModifier + } else { + Modifier.fillMaxWidth() + } if (content is ZoomableUrlContent) { mainImageModifier = mainImageModifier.combinedClickable( @@ -215,6 +220,7 @@ fun ZoomableContentView( title = content.description, artworkUri = content.artworkUri, authorName = content.authorName, + roundedCorner = roundedCorner, nostrUriCallback = content.uri, onDialog = { dialogOpen = true }, accountViewModel = accountViewModel @@ -228,6 +234,7 @@ fun ZoomableContentView( title = content.description, artworkUri = content.artworkUri, authorName = content.authorName, + roundedCorner = roundedCorner, nostrUriCallback = content.uri, onDialog = { dialogOpen = true }, accountViewModel = accountViewModel @@ -624,11 +631,11 @@ fun ZoomableImageDialog( pagerState = pagerState, itemsCount = allImages.size, itemContent = { index -> - RenderImageOrVideo(allImages[index], accountViewModel) + RenderImageOrVideo(allImages[index], false, accountViewModel) } ) } else { - RenderImageOrVideo(imageUrl, accountViewModel) + RenderImageOrVideo(imageUrl, false, accountViewModel) } Row( @@ -656,7 +663,7 @@ fun ZoomableImageDialog( } @Composable -private fun RenderImageOrVideo(content: ZoomableContent, accountViewModel: AccountViewModel) { +private fun RenderImageOrVideo(content: ZoomableContent, roundedCorner: Boolean, accountViewModel: AccountViewModel) { val mainModifier = Modifier .fillMaxSize() .zoomable(rememberZoomState()) @@ -670,6 +677,7 @@ private fun RenderImageOrVideo(content: ZoomableContent, accountViewModel: Accou title = content.description, artworkUri = content.artworkUri, authorName = content.authorName, + roundedCorner = roundedCorner, accountViewModel = accountViewModel, alwaysShowVideo = true ) @@ -684,6 +692,7 @@ private fun RenderImageOrVideo(content: ZoomableContent, accountViewModel: Accou title = content.description, artworkUri = content.artworkUri, authorName = content.authorName, + roundedCorner = roundedCorner, accountViewModel = accountViewModel, alwaysShowVideo = true ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/NotificationFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/NotificationFeedFilter.kt index 1e5369065..d28045c30 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/NotificationFeedFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/NotificationFeedFilter.kt @@ -40,7 +40,7 @@ class NotificationFeedFilter(val account: Account) : AdditiveFeedFilter() it.event !is BadgeDefinitionEvent && it.event !is BadgeProfilesEvent && it.event !is GiftWrapEvent && - it.author !== loggedInUser && + (it.event is LnZapEvent || it.author !== loggedInUser) && (isGlobal || it.author?.pubkeyHex in followingKeySet) && it.event?.isTaggedUser(loggedInUserHex) ?: false && (isHiddenList || it.author == null || !account.isHidden(it.author!!.pubkeyHex)) && diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index ccbef8894..f9ad25a4c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -481,8 +481,8 @@ fun NormalNote( ) } is BadgeDefinitionEvent -> BadgeDisplay(baseNote = baseNote) - is FileHeaderEvent -> FileHeaderDisplay(baseNote, accountViewModel) - is FileStorageHeaderEvent -> FileStorageHeaderDisplay(baseNote, accountViewModel) + is FileHeaderEvent -> FileHeaderDisplay(baseNote, isQuotedNote, accountViewModel) + is FileStorageHeaderEvent -> FileStorageHeaderDisplay(baseNote, isQuotedNote, accountViewModel) else -> LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup -> CheckNewAndRenderNote( @@ -3194,7 +3194,7 @@ private fun RenderBadge( } @Composable -fun FileHeaderDisplay(note: Note, accountViewModel: AccountViewModel) { +fun FileHeaderDisplay(note: Note, isQuotedNote: Boolean, accountViewModel: AccountViewModel) { val event = (note.event as? FileHeaderEvent) ?: return val fullUrl = event.url() ?: return @@ -3230,18 +3230,18 @@ fun FileHeaderDisplay(note: Note, accountViewModel: AccountViewModel) { } SensitivityWarning(note = note, accountViewModel = accountViewModel) { - ZoomableContentView(content = content, accountViewModel = accountViewModel) + ZoomableContentView(content = content, roundedCorner = isQuotedNote, accountViewModel = accountViewModel) } } @Composable -fun FileStorageHeaderDisplay(baseNote: Note, accountViewModel: AccountViewModel) { +fun FileStorageHeaderDisplay(baseNote: Note, isQuotedNote: Boolean, accountViewModel: AccountViewModel) { val eventHeader = (baseNote.event as? FileStorageHeaderEvent) ?: return val dataEventId = eventHeader.dataEventId() ?: return LoadNote(baseNoteHex = dataEventId) { contentNote -> if (contentNote != null) { - ObserverAndRenderNIP95(baseNote, contentNote, accountViewModel) + ObserverAndRenderNIP95(baseNote, contentNote, isQuotedNote, accountViewModel) } } } @@ -3250,6 +3250,7 @@ fun FileStorageHeaderDisplay(baseNote: Note, accountViewModel: AccountViewModel) private fun ObserverAndRenderNIP95( header: Note, content: Note, + isQuotedNote: Boolean, accountViewModel: AccountViewModel ) { val eventHeader = (header.event as? FileStorageHeaderEvent) ?: return @@ -3296,7 +3297,7 @@ private fun ObserverAndRenderNIP95( Crossfade(targetState = content) { if (it != null) { SensitivityWarning(note = header, accountViewModel = accountViewModel) { - ZoomableContentView(content = it, accountViewModel = accountViewModel) + ZoomableContentView(content = it, roundedCorner = isQuotedNote, accountViewModel = accountViewModel) } } } @@ -3367,6 +3368,7 @@ fun AudioTrackHeader(noteEvent: AudioTrackEvent, note: Note, accountViewModel: A title = noteEvent.subject(), thumbUri = cover, authorName = note.author?.toBestDisplayName(), + roundedCorner = true, nostrUriCallback = "nostr:${note.toNEvent()}", accountViewModel = accountViewModel ) @@ -3375,6 +3377,7 @@ fun AudioTrackHeader(noteEvent: AudioTrackEvent, note: Note, accountViewModel: A videoUri = media, title = noteEvent.subject(), authorName = note.author?.toBestDisplayName(), + roundedCorner = true, accountViewModel = accountViewModel ) } @@ -3387,9 +3390,23 @@ fun AudioTrackHeader(noteEvent: AudioTrackEvent, note: Note, accountViewModel: A fun AudioHeader(noteEvent: AudioHeaderEvent, note: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) { val media = remember { noteEvent.stream() ?: noteEvent.download() } val waveform = remember { noteEvent.wavefrom()?.toImmutableList()?.ifEmpty { null } } - val subject = remember { noteEvent.subject()?.ifBlank { null } } val content = remember { noteEvent.content().ifBlank { null } } + val defaultBackground = MaterialTheme.colors.background + val background = remember { mutableStateOf(defaultBackground) } + val tags = remember(noteEvent) { noteEvent?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() } + + val eventContent = remember(note.event) { + val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null } + val body = accountViewModel.decrypt(note) + + if (!subject.isNullOrBlank() && body?.split("\n")?.get(0)?.contains(subject) == false) { + "## $subject\n$body" + } else { + body + } + } + Row(modifier = Modifier.padding(top = 5.dp)) { Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { media?.let { media -> @@ -3401,42 +3418,30 @@ fun AudioHeader(noteEvent: AudioHeaderEvent, note: Note, accountViewModel: Accou waveform = waveform, title = noteEvent.subject(), authorName = note.author?.toBestDisplayName(), + roundedCorner = true, accountViewModel = accountViewModel, nostrUriCallback = note.toNostrUri() ) } } - Row() { - subject?.let { - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)) { - Text( - text = it, - fontWeight = FontWeight.Bold, - maxLines = 3, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.fillMaxWidth() - ) - } + content?.let { + Row(verticalAlignment = CenterVertically, modifier = Modifier.fillMaxWidth().padding(top = 5.dp)) { + TranslatableRichTextViewer( + content = it, + canPreview = true, + tags = tags, + backgroundColor = background, + accountViewModel = accountViewModel, + nav = nav + ) } } - Row() { - content?.let { - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)) { - Text( - text = it, - fontWeight = FontWeight.Bold, - maxLines = 3, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.fillMaxWidth() - ) - } - } + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + val hashtags = remember(noteEvent) { noteEvent.hashtags().toImmutableList() } + DisplayUncitedHashtags(hashtags, content ?: "", nav) } - - val hashtags = remember(noteEvent) { noteEvent.hashtags().toImmutableList() } - DisplayUncitedHashtags(hashtags, content ?: "", nav) } } } @@ -3559,6 +3564,7 @@ fun RenderLiveActivityEventInner(baseNote: Note, accountViewModel: AccountViewMo title = subject, artworkUri = cover, authorName = baseNote.author?.toBestDisplayName(), + roundedCorner = true, accountViewModel = accountViewModel, nostrUriCallback = "nostr:${baseNote.toNEvent()}" ) 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 2f19115f3..580c14d5b 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 @@ -377,9 +377,9 @@ fun NoteMaster( nav = nav ) } else if (noteEvent is FileHeaderEvent) { - FileHeaderDisplay(baseNote, accountViewModel) + FileHeaderDisplay(baseNote, true, accountViewModel) } else if (noteEvent is FileStorageHeaderEvent) { - FileStorageHeaderDisplay(baseNote, accountViewModel) + FileStorageHeaderDisplay(baseNote, true, accountViewModel) } else if (noteEvent is PeopleListEvent) { DisplayPeopleList(baseNote, backgroundColor, accountViewModel, nav) } else if (noteEvent is AudioTrackEvent) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt index 449c57b83..a653717cd 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt @@ -640,6 +640,7 @@ fun ShowVideoStreaming( ZoomableContentView( content = zoomableUrlVideo, + roundedCorner = false, accountViewModel = accountViewModel ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt index 1c075d859..2195ee070 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt @@ -302,9 +302,9 @@ private fun RenderVideoOrPictureNote( Row(remember { Modifier.weight(1f) }, verticalAlignment = Alignment.CenterVertically) { val noteEvent = remember { note.event } if (noteEvent is FileHeaderEvent) { - FileHeaderDisplay(note, accountViewModel) + FileHeaderDisplay(note, false, accountViewModel) } else if (noteEvent is FileStorageHeaderEvent) { - FileStorageHeaderDisplay(note, accountViewModel) + FileStorageHeaderDisplay(note, false, accountViewModel) } } }