diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 8f0c9aca..8f053b6c 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -388,7 +388,7 @@ 5053ACA72A56DF3B00851AE3 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */; }; 50A16FFB2AA6C06600DFEC1F /* DamusAVPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */; }; 50A16FFD2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */; }; - 50A16FFF2AA76A0900DFEC1F /* VideoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFE2AA76A0900DFEC1F /* VideoController.swift */; }; + 50A16FFF2AA76A0900DFEC1F /* DamusVideoCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFE2AA76A0900DFEC1F /* DamusVideoCoordinator.swift */; }; 50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; }; 50A60D142A28BEEE00186190 /* RelayLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A60D132A28BEEE00186190 /* RelayLog.swift */; }; 50B5685329F97CB400A23243 /* CredentialHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B5685229F97CB400A23243 /* CredentialHandler.swift */; }; @@ -784,7 +784,7 @@ D73E5ED72C6A97F4007EB227 /* MutinyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B62B9E692E00781E0A /* MutinyButton.swift */; }; D73E5ED82C6A97F4007EB227 /* DamusVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A2929DDF54400516EAC /* DamusVideoPlayer.swift */; }; D73E5ED92C6A97F4007EB227 /* DamusVideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */; }; - D73E5EDA2C6A97F4007EB227 /* VideoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFE2AA76A0900DFEC1F /* VideoController.swift */; }; + D73E5EDA2C6A97F4007EB227 /* DamusVideoCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFE2AA76A0900DFEC1F /* DamusVideoCoordinator.swift */; }; D73E5EDB2C6A97F4007EB227 /* DamusAVPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */; }; D73E5EDC2C6A97F4007EB227 /* ReactionsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C15C7142A55DE7A00D0A0DB /* ReactionsSettingsView.swift */; }; D73E5EDD2C6A97F4007EB227 /* NotificationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A1C29DDCF9B00516EAC /* NotificationSettingsView.swift */; }; @@ -1832,7 +1832,7 @@ 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingsView.swift; sourceTree = ""; }; 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusAVPlayerView.swift; sourceTree = ""; }; 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusVideoPlayerViewModel.swift; sourceTree = ""; }; - 50A16FFE2AA76A0900DFEC1F /* VideoController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoController.swift; sourceTree = ""; }; + 50A16FFE2AA76A0900DFEC1F /* DamusVideoCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusVideoCoordinator.swift; sourceTree = ""; }; 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = ""; }; 50A60D132A28BEEE00186190 /* RelayLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayLog.swift; sourceTree = ""; }; 50B5685229F97CB400A23243 /* CredentialHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialHandler.swift; sourceTree = ""; }; @@ -2294,7 +2294,7 @@ children = ( 4C1A9A2929DDF54400516EAC /* DamusVideoPlayer.swift */, 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */, - 50A16FFE2AA76A0900DFEC1F /* VideoController.swift */, + 50A16FFE2AA76A0900DFEC1F /* DamusVideoCoordinator.swift */, 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */, ); path = Video; @@ -3899,7 +3899,7 @@ 5C14C29F2BBBA5C600079FD2 /* RelayNipList.swift in Sources */, D78DB85B2C20FE5000F0AB12 /* VectorMath.swift in Sources */, D7CB5D3E2B116DAD00AD4105 /* NotificationsManager.swift in Sources */, - 50A16FFF2AA76A0900DFEC1F /* VideoController.swift in Sources */, + 50A16FFF2AA76A0900DFEC1F /* DamusVideoCoordinator.swift in Sources */, F7908E97298B1FDF00AB113A /* NIPURLBuilder.swift in Sources */, 4C285C8228385570008A31F1 /* CarouselView.swift in Sources */, 3A3040F129A8FF97008A0F29 /* LocalizationUtil.swift in Sources */, @@ -4452,7 +4452,7 @@ D73E5ED72C6A97F4007EB227 /* MutinyButton.swift in Sources */, D73E5ED82C6A97F4007EB227 /* DamusVideoPlayer.swift in Sources */, D73E5ED92C6A97F4007EB227 /* DamusVideoPlayerViewModel.swift in Sources */, - D73E5EDA2C6A97F4007EB227 /* VideoController.swift in Sources */, + D73E5EDA2C6A97F4007EB227 /* DamusVideoCoordinator.swift in Sources */, D73E5EDB2C6A97F4007EB227 /* DamusAVPlayerView.swift in Sources */, D73E5EDC2C6A97F4007EB227 /* ReactionsSettingsView.swift in Sources */, D73E5EDD2C6A97F4007EB227 /* NotificationSettingsView.swift in Sources */, diff --git a/damus/Components/ImageCarousel.swift b/damus/Components/ImageCarousel.swift index 4c1f9e66..0d2fbb7e 100644 --- a/damus/Components/ImageCarousel.swift +++ b/damus/Components/ImageCarousel.swift @@ -186,7 +186,7 @@ struct ImageCarousel: View { model.open_sheet = true } case .video(let url): - DamusVideoPlayer(url: url, video_size: $model.video_size, controller: state.video, style: .preview(on_tap: { model.open_sheet = true })) + DamusVideoPlayer(url: url, video_size: $model.video_size, coordinator: state.video, style: .preview(on_tap: { model.open_sheet = true })) .onChange(of: model.video_size) { size in guard let size else { return } @@ -257,14 +257,14 @@ struct ImageCarousel: View { .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) .fullScreenCover(isPresented: $model.open_sheet) { if let content { - FullScreenCarouselView(video_controller: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex) { + FullScreenCarouselView(video_coordinator: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex) { content({ // Dismiss closure model.open_sheet = false }) } } else { - FullScreenCarouselView(video_controller: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex) + FullScreenCarouselView(video_coordinator: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex) } } .frame(height: height) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 515e13f6..eeaf714c 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -686,7 +686,7 @@ struct ContentView: View { wallet: WalletModel(settings: settings), nav: self.navigationCoordinator, music: MusicController(onChange: music_changed), - video: VideoController(), + video: DamusVideoCoordinator(), ndb: ndb, quote_reposts: .init(our_pubkey: pubkey), emoji_provider: DefaultEmojiProvider(showAllVariations: true) diff --git a/damus/Models/DamusState.swift b/damus/Models/DamusState.swift index aee37f05..f77191f9 100644 --- a/damus/Models/DamusState.swift +++ b/damus/Models/DamusState.swift @@ -34,13 +34,13 @@ class DamusState: HeadlessDamusState { let wallet: WalletModel let nav: NavigationCoordinator let music: MusicController? - let video: VideoController + let video: DamusVideoCoordinator let ndb: Ndb var purple: DamusPurple var push_notification_client: PushNotificationClient let emoji_provider: EmojiProvider - init(pool: RelayPool, keypair: Keypair, likes: EventCounter, boosts: EventCounter, contacts: Contacts, mutelist_manager: MutelistManager, profiles: Profiles, dms: DirectMessagesModel, previews: PreviewCache, zaps: Zaps, lnurls: LNUrls, settings: UserSettingsStore, relay_filters: RelayFilters, relay_model_cache: RelayModelCache, drafts: Drafts, events: EventCache, bookmarks: BookmarksManager, postbox: PostBox, bootstrap_relays: [RelayURL], replies: ReplyCounter, wallet: WalletModel, nav: NavigationCoordinator, music: MusicController?, video: VideoController, ndb: Ndb, purple: DamusPurple? = nil, quote_reposts: EventCounter, emoji_provider: EmojiProvider) { + init(pool: RelayPool, keypair: Keypair, likes: EventCounter, boosts: EventCounter, contacts: Contacts, mutelist_manager: MutelistManager, profiles: Profiles, dms: DirectMessagesModel, previews: PreviewCache, zaps: Zaps, lnurls: LNUrls, settings: UserSettingsStore, relay_filters: RelayFilters, relay_model_cache: RelayModelCache, drafts: Drafts, events: EventCache, bookmarks: BookmarksManager, postbox: PostBox, bootstrap_relays: [RelayURL], replies: ReplyCounter, wallet: WalletModel, nav: NavigationCoordinator, music: MusicController?, video: DamusVideoCoordinator, ndb: Ndb, purple: DamusPurple? = nil, quote_reposts: EventCounter, emoji_provider: EmojiProvider) { self.pool = pool self.keypair = keypair self.likes = likes @@ -141,7 +141,7 @@ class DamusState: HeadlessDamusState { wallet: WalletModel(settings: settings), nav: navigationCoordinator, music: MusicController(onChange: { _ in }), - video: VideoController(), + video: DamusVideoCoordinator(), ndb: ndb, quote_reposts: .init(our_pubkey: pubkey), emoji_provider: DefaultEmojiProvider(showAllVariations: true) @@ -209,7 +209,7 @@ class DamusState: HeadlessDamusState { wallet: WalletModel(settings: UserSettingsStore()), nav: NavigationCoordinator(), music: nil, - video: VideoController(), + video: DamusVideoCoordinator(), ndb: .empty, quote_reposts: .init(our_pubkey: empty_pub), emoji_provider: DefaultEmojiProvider(showAllVariations: true) diff --git a/damus/Views/Images/FullScreenCarouselView.swift b/damus/Views/Images/FullScreenCarouselView.swift index 4da7d208..12a1898b 100644 --- a/damus/Views/Images/FullScreenCarouselView.swift +++ b/damus/Views/Images/FullScreenCarouselView.swift @@ -8,7 +8,7 @@ import SwiftUI struct FullScreenCarouselView: View { - let video_controller: VideoController + let video_coordinator: DamusVideoCoordinator let urls: [MediaUrl] @Environment(\.presentationMode) var presentationMode @@ -19,8 +19,8 @@ struct FullScreenCarouselView: View { @Binding var selectedIndex: Int let content: (() -> Content)? - init(video_controller: VideoController, urls: [MediaUrl], showMenu: Bool = true, settings: UserSettingsStore, selectedIndex: Binding, @ViewBuilder content: @escaping () -> Content) { - self.video_controller = video_controller + init(video_coordinator: DamusVideoCoordinator, urls: [MediaUrl], showMenu: Bool = true, settings: UserSettingsStore, selectedIndex: Binding, @ViewBuilder content: @escaping () -> Content) { + self.video_coordinator = video_coordinator self.urls = urls self._showMenu = State(initialValue: showMenu) self.settings = settings @@ -28,8 +28,8 @@ struct FullScreenCarouselView: View { self.content = content } - init(video_controller: VideoController, urls: [MediaUrl], showMenu: Bool = true, settings: UserSettingsStore, selectedIndex: Binding) { - self.video_controller = video_controller + init(video_coordinator: DamusVideoCoordinator, urls: [MediaUrl], showMenu: Bool = true, settings: UserSettingsStore, selectedIndex: Binding) { + self.video_coordinator = video_coordinator self.urls = urls self._showMenu = State(initialValue: showMenu) self.settings = settings @@ -59,7 +59,7 @@ struct FullScreenCarouselView: View { ForEach(urls.indices, id: \.self) { index in VStack { if case .video = urls[safe: index] { - ImageContainerView(video_controller: video_controller, url: urls[index], settings: settings, imageDict: $imageDict) + ImageContainerView(video_coordinator: video_coordinator, url: urls[index], settings: settings, imageDict: $imageDict) .clipped() // SwiftUI hack from https://stackoverflow.com/a/74401288 to make playback controls show up within the TabView .aspectRatio(contentMode: .fit) .padding(.top, Theme.safeAreaInsets?.top) @@ -71,7 +71,7 @@ struct FullScreenCarouselView: View { } else { ZoomableScrollView { - ImageContainerView(video_controller: video_controller, url: urls[index], settings: settings, imageDict: $imageDict) + ImageContainerView(video_coordinator: video_coordinator, url: urls[index], settings: settings, imageDict: $imageDict) .aspectRatio(contentMode: .fit) .padding(.top, Theme.safeAreaInsets?.top) .padding(.bottom, Theme.safeAreaInsets?.bottom) @@ -148,7 +148,7 @@ fileprivate struct FullScreenCarouselPreviewView: View { } var body: some View { - FullScreenCarouselView(video_controller: test_damus_state.video, urls: [test_video_url, url], settings: test_damus_state.settings, selectedIndex: $selectedIndex) { + FullScreenCarouselView(video_coordinator: test_damus_state.video, urls: [test_video_url, url], settings: test_damus_state.settings, selectedIndex: $selectedIndex) { self.custom_content?() } .environmentObject(OrientationTracker()) diff --git a/damus/Views/Images/ImageContainerView.swift b/damus/Views/Images/ImageContainerView.swift index d3d94ff2..aca6442e 100644 --- a/damus/Views/Images/ImageContainerView.swift +++ b/damus/Views/Images/ImageContainerView.swift @@ -10,7 +10,7 @@ import Kingfisher struct ImageContainerView: View { - let video_controller: VideoController + let video_coordinator: DamusVideoCoordinator let url: MediaUrl let settings: UserSettingsStore @@ -51,7 +51,7 @@ struct ImageContainerView: View { case .image(let url): Img(url: url) case .video(let url): - DamusVideoPlayer(url: url, video_size: .constant(nil), controller: video_controller, style: .full, visibility_tracking_method: .generic) + DamusVideoPlayer(url: url, video_size: .constant(nil), coordinator: video_coordinator, style: .full, visibility_tracking_method: .generic) } } } @@ -64,9 +64,9 @@ struct ImageContainerView_Previews: PreviewProvider { static var previews: some View { @State var imageDict: [URL: UIImage] = [:] Group { - ImageContainerView(video_controller: test_damus_state.video, url: .image(test_image_url), settings: test_damus_state.settings, imageDict: $imageDict) + ImageContainerView(video_coordinator: test_damus_state.video, url: .image(test_image_url), settings: test_damus_state.settings, imageDict: $imageDict) .previewDisplayName("Image") - ImageContainerView(video_controller: test_damus_state.video, url: .video(test_video_url), settings: test_damus_state.settings, imageDict: $imageDict) + ImageContainerView(video_coordinator: test_damus_state.video, url: .video(test_video_url), settings: test_damus_state.settings, imageDict: $imageDict) .previewDisplayName("Video") } .environmentObject(OrientationTracker()) diff --git a/damus/Views/Video/DamusVideoCoordinator.swift b/damus/Views/Video/DamusVideoCoordinator.swift new file mode 100644 index 00000000..2e89b916 --- /dev/null +++ b/damus/Views/Video/DamusVideoCoordinator.swift @@ -0,0 +1,53 @@ +// +// DamusVideoCoordinator.swift +// damus +// +// Created by Bryan Montz on 9/3/23. +// + +import Combine +import Foundation + +struct VideoMetadata { + let has_audio: Bool + let size: CGSize +} + +/// DamusVideoCoordinator is responsible for coordinating the various video players in the damus app. +/// The goals of this object are to: +/// - ensure some video playing states (such as mute state) are consistent across different video player view instances of the same video +/// - ensure only one video is playing at a time +/// - Provide global video playback controls to control the currently playing video +/// +/// This is used as a singleton object (one per DamusState), which gets passed around to video players, which can then interact with the coordinator to ensure an app-wide coherent experience +/// +/// A good analogy here is that video players and their models/states are like individual car drivers, and this coordinator is like a traffic control person that ensures cars don't crash each other. +final class DamusVideoCoordinator: ObservableObject { + private var mute_states: [URL: Bool] = [:] + private var metadatas: [URL: VideoMetadata] = [:] + + @Published var focused_model_id: UUID? + + func toggle_should_mute_video(url: URL) { + let state = mute_states[url] ?? true + mute_states[url] = !state + + objectWillChange.send() + } + + func should_mute_video(url: URL) -> Bool { + mute_states[url] ?? true + } + + func set_metadata(_ metadata: VideoMetadata, url: URL) { + metadatas[url] = metadata + } + + func metadata(for url: URL) -> VideoMetadata? { + metadatas[url] + } + + func size_for_url(_ url: URL) -> CGSize? { + metadatas[url]?.size + } +} diff --git a/damus/Views/Video/DamusVideoPlayer.swift b/damus/Views/Video/DamusVideoPlayer.swift index 6beaa924..fbc570a9 100644 --- a/damus/Views/Video/DamusVideoPlayer.swift +++ b/damus/Views/Video/DamusVideoPlayer.swift @@ -24,7 +24,7 @@ struct DamusVideoPlayer: View { let visibility_tracking_method: VisibilityTrackingMethod @State var isVisible: Bool = false - init(url: URL, video_size: Binding, controller: VideoController, style: Style, visibility_tracking_method: VisibilityTrackingMethod = .y_scroll) { + init(url: URL, video_size: Binding, coordinator: DamusVideoCoordinator, style: Style, visibility_tracking_method: VisibilityTrackingMethod = .y_scroll) { self.url = url let mute: Bool? if case .full = style { @@ -33,7 +33,7 @@ struct DamusVideoPlayer: View { else { mute = nil } - _model = StateObject(wrappedValue: DamusVideoPlayerViewModel(url: url, video_size: video_size, controller: controller, mute: mute)) + _model = StateObject(wrappedValue: DamusVideoPlayerViewModel(url: url, video_size: video_size, coordinator: coordinator, mute: mute)) self.visibility_tracking_method = visibility_tracking_method self.style = style } @@ -166,11 +166,11 @@ struct DamusVideoPlayer: View { struct DamusVideoPlayer_Previews: PreviewProvider { static var previews: some View { Group { - DamusVideoPlayer(url: URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!, video_size: .constant(nil), controller: VideoController(), style: .full) + DamusVideoPlayer(url: URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!, video_size: .constant(nil), coordinator: DamusVideoCoordinator(), style: .full) .environmentObject(OrientationTracker()) .previewDisplayName("Full video player") - DamusVideoPlayer(url: URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!, video_size: .constant(nil), controller: VideoController(), style: .preview(on_tap: nil)) + DamusVideoPlayer(url: URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!, video_size: .constant(nil), coordinator: DamusVideoCoordinator(), style: .preview(on_tap: nil)) .environmentObject(OrientationTracker()) .previewDisplayName("Preview video player") } diff --git a/damus/Views/Video/DamusVideoPlayerViewModel.swift b/damus/Views/Video/DamusVideoPlayerViewModel.swift index 524c0b72..28f019ab 100644 --- a/damus/Views/Video/DamusVideoPlayerViewModel.swift +++ b/damus/Views/Video/DamusVideoPlayerViewModel.swift @@ -28,7 +28,7 @@ final class DamusVideoPlayerViewModel: ObservableObject { private let url: URL private let player_item: AVPlayerItem let player: AVPlayer - fileprivate let controller: VideoController + fileprivate let coordinator: DamusVideoCoordinator let player_view_controller = AVPlayerViewController() let id = UUID() @@ -47,28 +47,28 @@ final class DamusVideoPlayerViewModel: ObservableObject { didSet { if is_scrolled_into_view && !oldValue { // we have just scrolled from out of view into view - controller.focused_model_id = id + coordinator.focused_model_id = id } else if !is_scrolled_into_view && oldValue { // we have just scrolled from in view to out of view - if controller.focused_model_id == id { - controller.focused_model_id = nil + if coordinator.focused_model_id == id { + coordinator.focused_model_id = nil } } } } - init(url: URL, video_size: Binding, controller: VideoController, mute: Bool? = nil) { + init(url: URL, video_size: Binding, coordinator: DamusVideoCoordinator, mute: Bool? = nil) { self.url = url player_item = AVPlayerItem(url: url) player = AVPlayer(playerItem: player_item) - self.controller = controller + self.coordinator = coordinator _video_size = video_size Task { await load() } - is_muted = mute ?? controller.should_mute_video(url: url) + is_muted = mute ?? coordinator.should_mute_video(url: url) player.isMuted = is_muted NotificationCenter.default.addObserver( @@ -78,7 +78,7 @@ final class DamusVideoPlayerViewModel: ObservableObject { object: player_item ) - controller.$focused_model_id + coordinator.$focused_model_id .sink { [weak self] model_id in model_id == self?.id ? self?.player.play() : self?.player.pause() } @@ -111,7 +111,7 @@ final class DamusVideoPlayerViewModel: ObservableObject { } private func load() async { - if let meta = controller.metadata(for: url) { + if let meta = coordinator.metadata(for: url) { has_audio = meta.has_audio video_size = meta.size } else { @@ -124,7 +124,7 @@ final class DamusVideoPlayerViewModel: ObservableObject { func did_tap_mute_button() { is_muted.toggle() player.isMuted = is_muted - controller.toggle_should_mute_video(url: url) + coordinator.toggle_should_mute_video(url: url) } func set_view_is_visible(_ is_visible: Bool) { diff --git a/damus/Views/Video/VideoController.swift b/damus/Views/Video/VideoController.swift deleted file mode 100644 index 9377d5cb..00000000 --- a/damus/Views/Video/VideoController.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// VideoController.swift -// damus -// -// Created by Bryan Montz on 9/3/23. -// - -import Combine -import Foundation - -struct VideoMetadata { - let has_audio: Bool - let size: CGSize -} - -final class VideoController: ObservableObject { - private var mute_states: [URL: Bool] = [:] - private var metadatas: [URL: VideoMetadata] = [:] - - @Published var focused_model_id: UUID? - - func toggle_should_mute_video(url: URL) { - let state = mute_states[url] ?? true - mute_states[url] = !state - - objectWillChange.send() - } - - func should_mute_video(url: URL) -> Bool { - mute_states[url] ?? true - } - - func set_metadata(_ metadata: VideoMetadata, url: URL) { - metadatas[url] = metadata - } - - func metadata(for url: URL) -> VideoMetadata? { - metadatas[url] - } - - func size_for_url(_ url: URL) -> CGSize? { - metadatas[url]?.size - } -}