From ed749c5879351692d96784e34b924bd62d1c0dac Mon Sep 17 00:00:00 2001 From: Terry Yiu Date: Sun, 23 Nov 2025 13:04:05 -0600 Subject: [PATCH] WIP NIP-A0 voice messages --- damus.xcodeproj/project.pbxproj | 33 ++++++++++++++ .../xcshareddata/swiftpm/Package.resolved | 11 ++++- damus/Core/Nostr/NostrKind.swift | 1 + .../Models/LoadableNostrEventView.swift | 2 +- .../Features/Events/Models/NoteContent.swift | 29 ++++++++++-- damus/Features/Events/NoteContentView.swift | 4 +- .../Profile/Models/ProfileModel.swift | 6 +-- .../Features/Profile/Views/ProfileView.swift | 8 +++- .../Timeline/Models/ContentFilters.swift | 3 ++ .../Features/Timeline/Models/HomeModel.swift | 25 ++++++++++- .../Media/Audio/DamusAudioPlayerView.swift | 44 +++++++++++++++++++ .../Media/Images/ImageContainerView.swift | 4 +- damus/Shared/Media/Models/ImageCarousel.swift | 5 ++- 13 files changed, 159 insertions(+), 16 deletions(-) create mode 100644 damus/Shared/Media/Audio/DamusAudioPlayerView.swift diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 8f086bcd..bb1f8c7f 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -33,6 +33,10 @@ 3A515C542DF5371D002D3B34 /* TrustedNetworkButtonTipViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A515C532DF5371D002D3B34 /* TrustedNetworkButtonTipViewStyle.swift */; }; 3A515C552DF5371D002D3B34 /* TrustedNetworkButtonTipViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A515C532DF5371D002D3B34 /* TrustedNetworkButtonTipViewStyle.swift */; }; 3A515C562DF5371D002D3B34 /* TrustedNetworkButtonTipViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A515C532DF5371D002D3B34 /* TrustedNetworkButtonTipViewStyle.swift */; }; + 3A7379D42E5D5C4A00DF8B4E /* DSWaveformImageViews in Frameworks */ = {isa = PBXBuildFile; productRef = 3A7379D32E5D5C4A00DF8B4E /* DSWaveformImageViews */; }; + 3A7379D72E5D5E1900DF8B4E /* DamusAudioPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7379D62E5D5E1900DF8B4E /* DamusAudioPlayerView.swift */; }; + 3A7379D82E5D5E1900DF8B4E /* DamusAudioPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7379D62E5D5E1900DF8B4E /* DamusAudioPlayerView.swift */; }; + 3A7379D92E5D5E1900DF8B4E /* DamusAudioPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7379D62E5D5E1900DF8B4E /* DamusAudioPlayerView.swift */; }; 3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8CC6CB2A2CFEF900940F5F /* StringUtil.swift */; }; 3A92C0FE2DE16E9800CEEBAC /* FaviconCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A92C0FD2DE16E9800CEEBAC /* FaviconCache.swift */; }; 3A92C0FF2DE16E9800CEEBAC /* FaviconCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A92C0FD2DE16E9800CEEBAC /* FaviconCache.swift */; }; @@ -1923,6 +1927,7 @@ 3A66D927299472FA008B44F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; 3A66D928299472FA008B44F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; 3A66D929299472FA008B44F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ja; path = ja.lproj/Localizable.stringsdict; sourceTree = ""; }; + 3A7379D62E5D5E1900DF8B4E /* DamusAudioPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusAudioPlayerView.swift; sourceTree = ""; }; 3A821C3E29E819D500B4BCA7 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; 3A821C3F29E819D500B4BCA7 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; 3A821C4029E819D500B4BCA7 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fr; path = fr.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -2731,6 +2736,7 @@ buildActionMask = 2147483647; files = ( 3ACF94382DA9A52F00971A4E /* FaviconFinder in Frameworks */, + 3A7379D42E5D5C4A00DF8B4E /* DSWaveformImageViews in Frameworks */, 4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */, D7DB1FE42D5A9AC900CF06DA /* CryptoSwift in Frameworks */, 3A0A30BB2C21397A00F8C9BC /* EmojiPicker in Frameworks */, @@ -2824,6 +2830,14 @@ path = Tips; sourceTree = ""; }; + 3A7379D52E5D5DF000DF8B4E /* Audio */ = { + isa = PBXGroup; + children = ( + 3A7379D62E5D5E1900DF8B4E /* DamusAudioPlayerView.swift */, + ); + path = Audio; + sourceTree = ""; + }; 3AA24800297E3DAE0090C62D /* Reposts */ = { isa = PBXGroup; children = ( @@ -4392,6 +4406,7 @@ children = ( 5C78A79D2E303D2600CF177D /* Models */, 4CFF8F6129CC9A80008DB934 /* Images */, + 3A7379D52E5D5DF000DF8B4E /* Audio */, 4C1A9A2829DDF53B00516EAC /* Video */, BA3759952ABCCF360018D73B /* Camera */, 4C198DEA29F88C6B004C165C /* BlurHash */, @@ -5060,6 +5075,7 @@ D7C48C0A2D12DE0C00A3BACF /* SwiftyCrop */, D7DB1FE32D5A9AC900CF06DA /* CryptoSwift */, 3ACF94372DA9A52F00971A4E /* FaviconFinder */, + 3A7379D32E5D5C4A00DF8B4E /* DSWaveformImageViews */, ); productName = damus; productReference = 4CE6DEE327F7A08100C66700 /* damus.app */; @@ -5271,6 +5287,7 @@ D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */, D7DB1FE22D5A9AC900CF06DA /* XCRemoteSwiftPackageReference "CryptoSwift" */, 3ACF94362DA9A52F00971A4E /* XCRemoteSwiftPackageReference "FaviconFinder" */, + 3A7379D22E5D5C4A00DF8B4E /* XCRemoteSwiftPackageReference "DSWaveformImage" */, ); productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */; projectDirPath = ""; @@ -5805,6 +5822,7 @@ E0EE9DD42B8E5FEA00F3002D /* ImageProcessing.swift in Sources */, 4CB883B0297705DD00DC99E7 /* NoteZapButton.swift in Sources */, D7DF58342DFCF18D00E9AD28 /* SendPaymentView.swift in Sources */, + 3A7379D72E5D5E1900DF8B4E /* DamusAudioPlayerView.swift in Sources */, 4C363A922825FCF2006E126D /* ProfileUpdate.swift in Sources */, 4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */, 4C32B9502A9AD44700DC3548 /* FlatBufferBuilder.swift in Sources */, @@ -6095,6 +6113,7 @@ 82D6FB092CD99F7900C925F4 /* SearchHeaderView.swift in Sources */, 82D6FB0A2CD99F7900C925F4 /* DamusGradient.swift in Sources */, D7DB93052D66A44100DA1EE5 /* Undistractor.swift in Sources */, + 3A7379D92E5D5E1900DF8B4E /* DamusAudioPlayerView.swift in Sources */, 82D6FB0C2CD99F7900C925F4 /* GoldSupportGradient.swift in Sources */, 82D6FB0D2CD99F7900C925F4 /* PinkGradient.swift in Sources */, 82D6FB0E2CD99F7900C925F4 /* GrayGradient.swift in Sources */, @@ -6780,6 +6799,7 @@ D73E5F092C6A97F4007EB227 /* ProfilePicView.swift in Sources */, D73E5F0A2C6A97F4007EB227 /* ProfileView.swift in Sources */, D73E5F0B2C6A97F4007EB227 /* ProfileNameView.swift in Sources */, + 3A7379D82E5D5E1900DF8B4E /* DamusAudioPlayerView.swift in Sources */, D73E5F0C2C6A97F4007EB227 /* MaybeAnonPfpView.swift in Sources */, D73E5F0D2C6A97F4007EB227 /* EventProfileName.swift in Sources */, D73E5F0E2C6A97F4007EB227 /* FriendIcon.swift in Sources */, @@ -7922,6 +7942,14 @@ minimumVersion = 0.2.0; }; }; + 3A7379D22E5D5C4A00DF8B4E /* XCRemoteSwiftPackageReference "DSWaveformImage" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/dmrschmidt/DSWaveformImage"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 14.2.2; + }; + }; 3ACF94362DA9A52F00971A4E /* XCRemoteSwiftPackageReference "FaviconFinder" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/will-lumley/FaviconFinder.git"; @@ -8010,6 +8038,11 @@ package = 3A0A30B92C21397A00F8C9BC /* XCRemoteSwiftPackageReference "EmojiPicker" */; productName = EmojiPicker; }; + 3A7379D32E5D5C4A00DF8B4E /* DSWaveformImageViews */ = { + isa = XCSwiftPackageProductDependency; + package = 3A7379D22E5D5C4A00DF8B4E /* XCRemoteSwiftPackageReference "DSWaveformImage" */; + productName = DSWaveformImageViews; + }; 3ACF94372DA9A52F00971A4E /* FaviconFinder */ = { isa = XCSwiftPackageProductDependency; package = 3ACF94362DA9A52F00971A4E /* XCRemoteSwiftPackageReference "FaviconFinder" */; diff --git a/damus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/damus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6de7b688..9f3f0ffa 100644 --- a/damus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/damus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "1fc7e0b44329ba72cd285eeb022b5b92582cd01586b920d243cb0485c2e69dcc", + "originHash" : "8d71e78d1d7bdc5a85a38932a14f84af755a9e34aeab19f9d540bd11a7b32fbc", "pins" : [ { "identity" : "codescanner", @@ -17,6 +17,15 @@ "revision" : "e74bbbfbef939224b242ae7c342a90e60b88b5ce" } }, + { + "identity" : "dswaveformimage", + "kind" : "remoteSourceControl", + "location" : "https://github.com/dmrschmidt/DSWaveformImage", + "state" : { + "revision" : "4c56578ee10128ee2b2c04c9c5aa73812de722db", + "version" : "14.2.2" + } + }, { "identity" : "emojikit", "kind" : "remoteSourceControl", diff --git a/damus/Core/Nostr/NostrKind.swift b/damus/Core/Nostr/NostrKind.swift index 7634aeff..ad127ae8 100644 --- a/damus/Core/Nostr/NostrKind.swift +++ b/damus/Core/Nostr/NostrKind.swift @@ -18,6 +18,7 @@ enum NostrKind: UInt32, Codable { case boost = 6 case like = 7 case chat = 42 + case voice_message = 1222 case mute_list = 10000 case relay_list = 10002 case interest_list = 10015 diff --git a/damus/Features/Events/Models/LoadableNostrEventView.swift b/damus/Features/Events/Models/LoadableNostrEventView.swift index 6d4b4ed3..859280d8 100644 --- a/damus/Features/Events/Models/LoadableNostrEventView.swift +++ b/damus/Features/Events/Models/LoadableNostrEventView.swift @@ -62,7 +62,7 @@ class LoadableNostrEventViewModel: ObservableObject { guard let ev = await self.loadEvent(noteId: note_id) else { return .not_found } guard let known_kind = ev.known_kind else { return .unknown_or_unsupported_kind } switch known_kind { - case .text, .highlight: + case .text, .highlight, .voice_message: return .loaded(route: Route.Thread(thread: ThreadModel(event: ev, damus_state: damus_state))) case .dm: let dm_model = damus_state.dms.lookup_or_create(ev.pubkey) diff --git a/damus/Features/Events/Models/NoteContent.swift b/damus/Features/Events/Models/NoteContent.swift index 78209d8d..3f544f7e 100644 --- a/damus/Features/Events/Models/NoteContent.swift +++ b/damus/Features/Events/Models/NoteContent.swift @@ -319,6 +319,8 @@ func classify_url(_ url: URL) -> UrlType { return .media(.image(url)) case "mp4", "mov", "m3u8": return .media(.video(url)) + case "m4a": + return .media(.audio(url)) default: return .link(url) } @@ -452,6 +454,8 @@ enum UrlType { return url case .video(let url): return url + case .audio(let url): + return url } case .link(let url): return url @@ -462,7 +466,7 @@ enum UrlType { switch self { case .media(let media_url): switch media_url { - case .image: + case .image, .audio: return nil case .video(let url): return url @@ -478,14 +482,28 @@ enum UrlType { switch media_url { case .image(let url): return url - case .video: + case .video, .audio: return nil } case .link: return nil } } - + + var is_audio: URL? { + switch self { + case .media(let media_url): + switch media_url { + case .audio(let url): + return url + case .image, .video: + return nil + } + case .link: + return nil + } + } + var is_link: URL? { switch self { case .media: @@ -508,13 +526,16 @@ enum UrlType { enum MediaUrl { case image(URL) case video(URL) - + case audio(URL) + var url: URL { switch self { case .image(let url): return url case .video(let url): return url + case .audio(let url): + return url } } } diff --git a/damus/Features/Events/NoteContentView.swift b/damus/Features/Events/NoteContentView.swift index 827718f7..00a55693 100644 --- a/damus/Features/Events/NoteContentView.swift +++ b/damus/Features/Events/NoteContentView.swift @@ -253,7 +253,7 @@ struct NoteContentView: View { Divider() .frame(height: 1) switch artifacts.media[index] { - case .image(let url), .video(let url): + case .image(let url), .video(let url), .audio(let url): Text(abbreviateURL(url)) .font(eventviewsize_to_font(size, font_size: damus_state.settings.font_size)) .foregroundStyle(DamusColors.neutral6) @@ -477,7 +477,7 @@ struct BlurOverlayView: View { let damus_state = damus_state { switch artifacts.media[0] { - case .image(let url), .video(let url): + case .image(let url), .video(let url), .audio(let url): Text(abbreviateURL(url, maxLength: 30)) .font(eventviewsize_to_font(size, font_size: damus_state.settings.font_size * 0.8)) .foregroundStyle(.white) diff --git a/damus/Features/Profile/Models/ProfileModel.swift b/damus/Features/Profile/Models/ProfileModel.swift index 0bfb5519..7350a568 100644 --- a/damus/Features/Profile/Models/ProfileModel.swift +++ b/damus/Features/Profile/Models/ProfileModel.swift @@ -75,7 +75,7 @@ class ProfileModel: ObservableObject, Equatable { } func subscribe() { - var text_filter = NostrFilter(kinds: [.text, .longform, .highlight]) + var text_filter = NostrFilter(kinds: [.text, .longform, .highlight, .voice_message]) var profile_filter = NostrFilter(kinds: [.contacts, .metadata, .boost]) var relay_list_filter = NostrFilter(kinds: [.relay_list], authors: [pubkey]) @@ -98,7 +98,7 @@ class ProfileModel: ObservableObject, Equatable { return } - let conversation_kinds: [NostrKind] = [.text, .longform, .highlight] + let conversation_kinds: [NostrKind] = [.text, .longform, .highlight, .voice_message] let limit: UInt32 = 500 let conversations_filter_them = NostrFilter(kinds: conversation_kinds, pubkeys: [damus.pubkey], limit: limit, authors: [pubkey]) let conversations_filter_us = NostrFilter(kinds: conversation_kinds, pubkeys: [pubkey], limit: limit, authors: [damus.pubkey]) @@ -122,7 +122,7 @@ class ProfileModel: ObservableObject, Equatable { } private func add_event(_ ev: NostrEvent) { - if ev.is_textlike || ev.known_kind == .boost { + if ev.is_textlike || ev.known_kind == .boost || ev.known_kind == .voice_message { if self.events.insert(ev) { self.objectWillChange.send() } diff --git a/damus/Features/Profile/Views/ProfileView.swift b/damus/Features/Profile/Views/ProfileView.swift index 2c6f60d0..6d5c1783 100644 --- a/damus/Features/Profile/Views/ProfileView.swift +++ b/damus/Features/Profile/Views/ProfileView.swift @@ -123,7 +123,7 @@ struct ProfileView: View { var filters = ContentFilters.defaults(damus_state: damus_state) filters.append(fstate.filter) switch fstate { - case .posts, .posts_and_replies, .follow_list: + case .posts, .posts_and_replies, .follow_list, .voice_messages: filters.append({ profile.pubkey == $0.pubkey }) case .conversations: filters.append({ profile.conversation_events.contains($0.id) } ) @@ -438,7 +438,8 @@ struct ProfileView: View { var tabs: [(String, FilterState)] { var tabs = [ (NSLocalizedString("Notes", comment: "Label for filter for seeing only notes (instead of notes and replies)."), FilterState.posts), - (NSLocalizedString("Notes & Replies", comment: "Label for filter for seeing notes and replies (instead of only notes)."), FilterState.posts_and_replies) + (NSLocalizedString("Notes & Replies", comment: "Label for filter for seeing notes and replies (instead of only notes)."), FilterState.posts_and_replies), + (NSLocalizedString("Voice", comment: "Label for filter for seeing voice messages."), FilterState.voice_messages) ] if profile.pubkey != damus_state.pubkey && !profile.conversation_events.isEmpty { tabs.append((NSLocalizedString("Conversations", comment: "Label for filter for seeing notes and replies that involve conversations between the signed in user and the current profile."), FilterState.conversations)) @@ -469,6 +470,9 @@ struct ProfileView: View { if filter_state == FilterState.posts_and_replies { InnerTimelineView(events: profile.events, damus: damus_state, filter: content_filter(FilterState.posts_and_replies)) } + if filter_state == FilterState.voice_messages { + InnerTimelineView(events: profile.events, damus: damus_state, filter: content_filter(FilterState.voice_messages)) + } if filter_state == FilterState.conversations && !profile.conversation_events.isEmpty { InnerTimelineView(events: profile.events, damus: damus_state, filter: content_filter(FilterState.conversations)) } diff --git a/damus/Features/Timeline/Models/ContentFilters.swift b/damus/Features/Timeline/Models/ContentFilters.swift index a950b0db..dad58a31 100644 --- a/damus/Features/Timeline/Models/ContentFilters.swift +++ b/damus/Features/Timeline/Models/ContentFilters.swift @@ -14,6 +14,7 @@ enum FilterState : Int { case posts_and_replies = 1 case conversations = 2 case follow_list = 3 + case voice_messages = 4 func filter(ev: NostrEvent) -> Bool { switch self { @@ -25,6 +26,8 @@ enum FilterState : Int { return true case .follow_list: return ev.known_kind == .follow_list + case .voice_messages: + return ev.known_kind == .voice_message } } } diff --git a/damus/Features/Timeline/Models/HomeModel.swift b/damus/Features/Timeline/Models/HomeModel.swift index a18fd438..12d3473c 100644 --- a/damus/Features/Timeline/Models/HomeModel.swift +++ b/damus/Features/Timeline/Models/HomeModel.swift @@ -192,6 +192,8 @@ class HomeModel: ContactsDelegate { switch kind { case .chat, .longform, .text, .highlight: handle_text_event(sub_id: sub_id, ev) + case .voice_message: + handle_voice_message_event(sub_id: sub_id, ev) case .contacts: handle_contact_event(sub_id: sub_id, relay_id: relay_id, ev: ev) case .metadata: @@ -776,7 +778,28 @@ class HomeModel: ContactsDelegate { handle_notification(ev: ev) } } - + + func handle_voice_message_event(sub_id: String, _ ev: NostrEvent) { + guard should_show_event(state: damus_state, ev: ev) else { + return + } + + // TODO: will we need to process this in other places like zap request contents, etc? + process_image_metadatas(cache: damus_state.events, ev: ev) + damus_state.replies.count_replies(ev, keypair: self.damus_state.keypair) + damus_state.events.insert(ev) + + if let quoted_event = ev.referenced_quote_ids.first { + handle_quote_repost_event(ev, target: quoted_event.note_id) + } + + if sub_id == home_subid { + insert_home_event(ev) + } else if sub_id == notifications_subid { + handle_notification(ev: ev) + } + } + func got_new_dm(notifs: NewEventsBits, ev: NostrEvent) { notification_status.new_events = notifs diff --git a/damus/Shared/Media/Audio/DamusAudioPlayerView.swift b/damus/Shared/Media/Audio/DamusAudioPlayerView.swift new file mode 100644 index 00000000..f3c215e6 --- /dev/null +++ b/damus/Shared/Media/Audio/DamusAudioPlayerView.swift @@ -0,0 +1,44 @@ +// +// DamusAudioPlayerView.swift +// damus +// +// Created by Terry Yiu on 8/25/25. +// + +import DSWaveformImageViews +import SwiftUI + +struct DamusAudioPlayerView: View { + let remoteURL: URL + @State var localURL: URL + + var body: some View { + GeometryReader { geometry in + WaveformView(audioURL: localURL) { shape in + shape.fill(.white) + shape.fill(.red).mask(alignment: .leading) { + Rectangle().frame(width: geometry.size.width * progress) + } + } + } + + Text("Hello, World!") + .task { + URLSession.shared.downloadTask(with: remoteURL) { downloadedURL, urlResponse, error in + guard let downloadedURL = downloadedURL else { return } + + let cachesFolderURL = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false) + let audioFileURL = cachesFolderURL!.appendingPathComponent("yourLocalAudioFile.m4a") + try? FileManager.default.copyItem(at: downloadedURL, to: audioFileURL) + + DispatchQueue.main.async { + self.localURL = audioFileURL +// self.WaveformView.waveformAudioURL = audioFileURL + } + } + } +} + +#Preview { + DamusAudioPlayerView() +} diff --git a/damus/Shared/Media/Images/ImageContainerView.swift b/damus/Shared/Media/Images/ImageContainerView.swift index f13dc6ce..077ed556 100644 --- a/damus/Shared/Media/Images/ImageContainerView.swift +++ b/damus/Shared/Media/Images/ImageContainerView.swift @@ -57,7 +57,9 @@ struct ImageContainerView: View { switch url { case .image(let url): Img(url: url) - case .video(let url): + case .audio(let url): + DamusAudioPlayerView() + case .video(let url): DamusVideoPlayerView(url: url, coordinator: video_coordinator, style: .no_controls(on_tap: nil)) } } diff --git a/damus/Shared/Media/Models/ImageCarousel.swift b/damus/Shared/Media/Models/ImageCarousel.swift index 163d80a2..c3636f57 100644 --- a/damus/Shared/Media/Models/ImageCarousel.swift +++ b/damus/Shared/Media/Models/ImageCarousel.swift @@ -223,7 +223,7 @@ class CarouselModel: ObservableObject { private func observe_video_sizes() { for media_url in urls { switch media_url { - case .video(let url): + case .video(let url), .audio(let url): let video_player = damus_state.video.get_player(for: url) if let video_size = video_player.video_size { self.media_size_information[url] = video_size // Set the initial size if available @@ -302,6 +302,7 @@ class CarouselModel: ObservableObject { struct ImageCarousel: View { /// The event id of the note that this carousel is displaying let evid: NoteId + let event: NostrEvent /// The model that holds information and state of this carousel /// This is observed to update the view when the model changes @ObservedObject var model: CarouselModel @@ -354,6 +355,8 @@ struct ImageCarousel: View { .onTapGesture { present(full_screen_item: .full_screen_carousel(urls: model.urls, selectedIndex: $model.selectedIndex)) } + case .audio(let url): + DamusAudioPlayerView() case .video(let url): let video_model = model.damus_state.video.get_player(for: url) DamusVideoPlayerView(