Rename VideoController to DamusVideoCoordinator

This commit renames this class to better represent what it does.

This reduces some of the term overloading between this class and other video
controller classes/structs. (Such as AVPlayerController)

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
Daniel D’Aquino
2024-10-18 12:46:11 -07:00
parent c4ee52fdac
commit 290152c859
10 changed files with 93 additions and 84 deletions

View File

@@ -388,7 +388,7 @@
5053ACA72A56DF3B00851AE3 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */; }; 5053ACA72A56DF3B00851AE3 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */; };
50A16FFB2AA6C06600DFEC1F /* DamusAVPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */; }; 50A16FFB2AA6C06600DFEC1F /* DamusAVPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */; };
50A16FFD2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.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 */; }; 50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
50A60D142A28BEEE00186190 /* RelayLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A60D132A28BEEE00186190 /* RelayLog.swift */; }; 50A60D142A28BEEE00186190 /* RelayLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A60D132A28BEEE00186190 /* RelayLog.swift */; };
50B5685329F97CB400A23243 /* CredentialHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B5685229F97CB400A23243 /* CredentialHandler.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 */; }; D73E5ED72C6A97F4007EB227 /* MutinyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B62B9E692E00781E0A /* MutinyButton.swift */; };
D73E5ED82C6A97F4007EB227 /* DamusVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A2929DDF54400516EAC /* DamusVideoPlayer.swift */; }; D73E5ED82C6A97F4007EB227 /* DamusVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A2929DDF54400516EAC /* DamusVideoPlayer.swift */; };
D73E5ED92C6A97F4007EB227 /* DamusVideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.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 */; }; D73E5EDB2C6A97F4007EB227 /* DamusAVPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */; };
D73E5EDC2C6A97F4007EB227 /* ReactionsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C15C7142A55DE7A00D0A0DB /* ReactionsSettingsView.swift */; }; D73E5EDC2C6A97F4007EB227 /* ReactionsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C15C7142A55DE7A00D0A0DB /* ReactionsSettingsView.swift */; };
D73E5EDD2C6A97F4007EB227 /* NotificationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A1C29DDCF9B00516EAC /* NotificationSettingsView.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 = "<group>"; }; 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingsView.swift; sourceTree = "<group>"; };
50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusAVPlayerView.swift; sourceTree = "<group>"; }; 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusAVPlayerView.swift; sourceTree = "<group>"; };
50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusVideoPlayerViewModel.swift; sourceTree = "<group>"; }; 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusVideoPlayerViewModel.swift; sourceTree = "<group>"; };
50A16FFE2AA76A0900DFEC1F /* VideoController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoController.swift; sourceTree = "<group>"; }; 50A16FFE2AA76A0900DFEC1F /* DamusVideoCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusVideoCoordinator.swift; sourceTree = "<group>"; };
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; }; 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
50A60D132A28BEEE00186190 /* RelayLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayLog.swift; sourceTree = "<group>"; }; 50A60D132A28BEEE00186190 /* RelayLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayLog.swift; sourceTree = "<group>"; };
50B5685229F97CB400A23243 /* CredentialHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialHandler.swift; sourceTree = "<group>"; }; 50B5685229F97CB400A23243 /* CredentialHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialHandler.swift; sourceTree = "<group>"; };
@@ -2294,7 +2294,7 @@
children = ( children = (
4C1A9A2929DDF54400516EAC /* DamusVideoPlayer.swift */, 4C1A9A2929DDF54400516EAC /* DamusVideoPlayer.swift */,
50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */, 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */,
50A16FFE2AA76A0900DFEC1F /* VideoController.swift */, 50A16FFE2AA76A0900DFEC1F /* DamusVideoCoordinator.swift */,
50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */, 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */,
); );
path = Video; path = Video;
@@ -3899,7 +3899,7 @@
5C14C29F2BBBA5C600079FD2 /* RelayNipList.swift in Sources */, 5C14C29F2BBBA5C600079FD2 /* RelayNipList.swift in Sources */,
D78DB85B2C20FE5000F0AB12 /* VectorMath.swift in Sources */, D78DB85B2C20FE5000F0AB12 /* VectorMath.swift in Sources */,
D7CB5D3E2B116DAD00AD4105 /* NotificationsManager.swift in Sources */, D7CB5D3E2B116DAD00AD4105 /* NotificationsManager.swift in Sources */,
50A16FFF2AA76A0900DFEC1F /* VideoController.swift in Sources */, 50A16FFF2AA76A0900DFEC1F /* DamusVideoCoordinator.swift in Sources */,
F7908E97298B1FDF00AB113A /* NIPURLBuilder.swift in Sources */, F7908E97298B1FDF00AB113A /* NIPURLBuilder.swift in Sources */,
4C285C8228385570008A31F1 /* CarouselView.swift in Sources */, 4C285C8228385570008A31F1 /* CarouselView.swift in Sources */,
3A3040F129A8FF97008A0F29 /* LocalizationUtil.swift in Sources */, 3A3040F129A8FF97008A0F29 /* LocalizationUtil.swift in Sources */,
@@ -4452,7 +4452,7 @@
D73E5ED72C6A97F4007EB227 /* MutinyButton.swift in Sources */, D73E5ED72C6A97F4007EB227 /* MutinyButton.swift in Sources */,
D73E5ED82C6A97F4007EB227 /* DamusVideoPlayer.swift in Sources */, D73E5ED82C6A97F4007EB227 /* DamusVideoPlayer.swift in Sources */,
D73E5ED92C6A97F4007EB227 /* DamusVideoPlayerViewModel.swift in Sources */, D73E5ED92C6A97F4007EB227 /* DamusVideoPlayerViewModel.swift in Sources */,
D73E5EDA2C6A97F4007EB227 /* VideoController.swift in Sources */, D73E5EDA2C6A97F4007EB227 /* DamusVideoCoordinator.swift in Sources */,
D73E5EDB2C6A97F4007EB227 /* DamusAVPlayerView.swift in Sources */, D73E5EDB2C6A97F4007EB227 /* DamusAVPlayerView.swift in Sources */,
D73E5EDC2C6A97F4007EB227 /* ReactionsSettingsView.swift in Sources */, D73E5EDC2C6A97F4007EB227 /* ReactionsSettingsView.swift in Sources */,
D73E5EDD2C6A97F4007EB227 /* NotificationSettingsView.swift in Sources */, D73E5EDD2C6A97F4007EB227 /* NotificationSettingsView.swift in Sources */,

View File

@@ -186,7 +186,7 @@ struct ImageCarousel<Content: View>: View {
model.open_sheet = true model.open_sheet = true
} }
case .video(let url): 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 .onChange(of: model.video_size) { size in
guard let size else { return } guard let size else { return }
@@ -257,14 +257,14 @@ struct ImageCarousel<Content: View>: View {
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.fullScreenCover(isPresented: $model.open_sheet) { .fullScreenCover(isPresented: $model.open_sheet) {
if let content { if let content {
FullScreenCarouselView<Content>(video_controller: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex) { FullScreenCarouselView<Content>(video_coordinator: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex) {
content({ // Dismiss closure content({ // Dismiss closure
model.open_sheet = false model.open_sheet = false
}) })
} }
} }
else { else {
FullScreenCarouselView<AnyView>(video_controller: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex) FullScreenCarouselView<AnyView>(video_coordinator: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex)
} }
} }
.frame(height: height) .frame(height: height)

View File

@@ -686,7 +686,7 @@ struct ContentView: View {
wallet: WalletModel(settings: settings), wallet: WalletModel(settings: settings),
nav: self.navigationCoordinator, nav: self.navigationCoordinator,
music: MusicController(onChange: music_changed), music: MusicController(onChange: music_changed),
video: VideoController(), video: DamusVideoCoordinator(),
ndb: ndb, ndb: ndb,
quote_reposts: .init(our_pubkey: pubkey), quote_reposts: .init(our_pubkey: pubkey),
emoji_provider: DefaultEmojiProvider(showAllVariations: true) emoji_provider: DefaultEmojiProvider(showAllVariations: true)

View File

@@ -34,13 +34,13 @@ class DamusState: HeadlessDamusState {
let wallet: WalletModel let wallet: WalletModel
let nav: NavigationCoordinator let nav: NavigationCoordinator
let music: MusicController? let music: MusicController?
let video: VideoController let video: DamusVideoCoordinator
let ndb: Ndb let ndb: Ndb
var purple: DamusPurple var purple: DamusPurple
var push_notification_client: PushNotificationClient var push_notification_client: PushNotificationClient
let emoji_provider: EmojiProvider 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.pool = pool
self.keypair = keypair self.keypair = keypair
self.likes = likes self.likes = likes
@@ -141,7 +141,7 @@ class DamusState: HeadlessDamusState {
wallet: WalletModel(settings: settings), wallet: WalletModel(settings: settings),
nav: navigationCoordinator, nav: navigationCoordinator,
music: MusicController(onChange: { _ in }), music: MusicController(onChange: { _ in }),
video: VideoController(), video: DamusVideoCoordinator(),
ndb: ndb, ndb: ndb,
quote_reposts: .init(our_pubkey: pubkey), quote_reposts: .init(our_pubkey: pubkey),
emoji_provider: DefaultEmojiProvider(showAllVariations: true) emoji_provider: DefaultEmojiProvider(showAllVariations: true)
@@ -209,7 +209,7 @@ class DamusState: HeadlessDamusState {
wallet: WalletModel(settings: UserSettingsStore()), wallet: WalletModel(settings: UserSettingsStore()),
nav: NavigationCoordinator(), nav: NavigationCoordinator(),
music: nil, music: nil,
video: VideoController(), video: DamusVideoCoordinator(),
ndb: .empty, ndb: .empty,
quote_reposts: .init(our_pubkey: empty_pub), quote_reposts: .init(our_pubkey: empty_pub),
emoji_provider: DefaultEmojiProvider(showAllVariations: true) emoji_provider: DefaultEmojiProvider(showAllVariations: true)

View File

@@ -8,7 +8,7 @@
import SwiftUI import SwiftUI
struct FullScreenCarouselView<Content: View>: View { struct FullScreenCarouselView<Content: View>: View {
let video_controller: VideoController let video_coordinator: DamusVideoCoordinator
let urls: [MediaUrl] let urls: [MediaUrl]
@Environment(\.presentationMode) var presentationMode @Environment(\.presentationMode) var presentationMode
@@ -19,8 +19,8 @@ struct FullScreenCarouselView<Content: View>: View {
@Binding var selectedIndex: Int @Binding var selectedIndex: Int
let content: (() -> Content)? let content: (() -> Content)?
init(video_controller: VideoController, urls: [MediaUrl], showMenu: Bool = true, settings: UserSettingsStore, selectedIndex: Binding<Int>, @ViewBuilder content: @escaping () -> Content) { init(video_coordinator: DamusVideoCoordinator, urls: [MediaUrl], showMenu: Bool = true, settings: UserSettingsStore, selectedIndex: Binding<Int>, @ViewBuilder content: @escaping () -> Content) {
self.video_controller = video_controller self.video_coordinator = video_coordinator
self.urls = urls self.urls = urls
self._showMenu = State(initialValue: showMenu) self._showMenu = State(initialValue: showMenu)
self.settings = settings self.settings = settings
@@ -28,8 +28,8 @@ struct FullScreenCarouselView<Content: View>: View {
self.content = content self.content = content
} }
init(video_controller: VideoController, urls: [MediaUrl], showMenu: Bool = true, settings: UserSettingsStore, selectedIndex: Binding<Int>) { init(video_coordinator: DamusVideoCoordinator, urls: [MediaUrl], showMenu: Bool = true, settings: UserSettingsStore, selectedIndex: Binding<Int>) {
self.video_controller = video_controller self.video_coordinator = video_coordinator
self.urls = urls self.urls = urls
self._showMenu = State(initialValue: showMenu) self._showMenu = State(initialValue: showMenu)
self.settings = settings self.settings = settings
@@ -59,7 +59,7 @@ struct FullScreenCarouselView<Content: View>: View {
ForEach(urls.indices, id: \.self) { index in ForEach(urls.indices, id: \.self) { index in
VStack { VStack {
if case .video = urls[safe: index] { 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 .clipped() // SwiftUI hack from https://stackoverflow.com/a/74401288 to make playback controls show up within the TabView
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.padding(.top, Theme.safeAreaInsets?.top) .padding(.top, Theme.safeAreaInsets?.top)
@@ -71,7 +71,7 @@ struct FullScreenCarouselView<Content: View>: View {
} }
else { else {
ZoomableScrollView { 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) .aspectRatio(contentMode: .fit)
.padding(.top, Theme.safeAreaInsets?.top) .padding(.top, Theme.safeAreaInsets?.top)
.padding(.bottom, Theme.safeAreaInsets?.bottom) .padding(.bottom, Theme.safeAreaInsets?.bottom)
@@ -148,7 +148,7 @@ fileprivate struct FullScreenCarouselPreviewView<Content: View>: View {
} }
var body: some 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?() self.custom_content?()
} }
.environmentObject(OrientationTracker()) .environmentObject(OrientationTracker())

View File

@@ -10,7 +10,7 @@ import Kingfisher
struct ImageContainerView: View { struct ImageContainerView: View {
let video_controller: VideoController let video_coordinator: DamusVideoCoordinator
let url: MediaUrl let url: MediaUrl
let settings: UserSettingsStore let settings: UserSettingsStore
@@ -51,7 +51,7 @@ struct ImageContainerView: View {
case .image(let url): case .image(let url):
Img(url: url) Img(url: url)
case .video(let 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 { static var previews: some View {
@State var imageDict: [URL: UIImage] = [:] @State var imageDict: [URL: UIImage] = [:]
Group { 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") .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") .previewDisplayName("Video")
} }
.environmentObject(OrientationTracker()) .environmentObject(OrientationTracker())

View File

@@ -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
}
}

View File

@@ -24,7 +24,7 @@ struct DamusVideoPlayer: View {
let visibility_tracking_method: VisibilityTrackingMethod let visibility_tracking_method: VisibilityTrackingMethod
@State var isVisible: Bool = false @State var isVisible: Bool = false
init(url: URL, video_size: Binding<CGSize?>, controller: VideoController, style: Style, visibility_tracking_method: VisibilityTrackingMethod = .y_scroll) { init(url: URL, video_size: Binding<CGSize?>, coordinator: DamusVideoCoordinator, style: Style, visibility_tracking_method: VisibilityTrackingMethod = .y_scroll) {
self.url = url self.url = url
let mute: Bool? let mute: Bool?
if case .full = style { if case .full = style {
@@ -33,7 +33,7 @@ struct DamusVideoPlayer: View {
else { else {
mute = nil 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.visibility_tracking_method = visibility_tracking_method
self.style = style self.style = style
} }
@@ -166,11 +166,11 @@ struct DamusVideoPlayer: View {
struct DamusVideoPlayer_Previews: PreviewProvider { struct DamusVideoPlayer_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
Group { 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()) .environmentObject(OrientationTracker())
.previewDisplayName("Full video player") .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()) .environmentObject(OrientationTracker())
.previewDisplayName("Preview video player") .previewDisplayName("Preview video player")
} }

View File

@@ -28,7 +28,7 @@ final class DamusVideoPlayerViewModel: ObservableObject {
private let url: URL private let url: URL
private let player_item: AVPlayerItem private let player_item: AVPlayerItem
let player: AVPlayer let player: AVPlayer
fileprivate let controller: VideoController fileprivate let coordinator: DamusVideoCoordinator
let player_view_controller = AVPlayerViewController() let player_view_controller = AVPlayerViewController()
let id = UUID() let id = UUID()
@@ -47,28 +47,28 @@ final class DamusVideoPlayerViewModel: ObservableObject {
didSet { didSet {
if is_scrolled_into_view && !oldValue { if is_scrolled_into_view && !oldValue {
// we have just scrolled from out of view into view // 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 { } else if !is_scrolled_into_view && oldValue {
// we have just scrolled from in view to out of view // we have just scrolled from in view to out of view
if controller.focused_model_id == id { if coordinator.focused_model_id == id {
controller.focused_model_id = nil coordinator.focused_model_id = nil
} }
} }
} }
} }
init(url: URL, video_size: Binding<CGSize?>, controller: VideoController, mute: Bool? = nil) { init(url: URL, video_size: Binding<CGSize?>, coordinator: DamusVideoCoordinator, mute: Bool? = nil) {
self.url = url self.url = url
player_item = AVPlayerItem(url: url) player_item = AVPlayerItem(url: url)
player = AVPlayer(playerItem: player_item) player = AVPlayer(playerItem: player_item)
self.controller = controller self.coordinator = coordinator
_video_size = video_size _video_size = video_size
Task { Task {
await load() await load()
} }
is_muted = mute ?? controller.should_mute_video(url: url) is_muted = mute ?? coordinator.should_mute_video(url: url)
player.isMuted = is_muted player.isMuted = is_muted
NotificationCenter.default.addObserver( NotificationCenter.default.addObserver(
@@ -78,7 +78,7 @@ final class DamusVideoPlayerViewModel: ObservableObject {
object: player_item object: player_item
) )
controller.$focused_model_id coordinator.$focused_model_id
.sink { [weak self] model_id in .sink { [weak self] model_id in
model_id == self?.id ? self?.player.play() : self?.player.pause() model_id == self?.id ? self?.player.play() : self?.player.pause()
} }
@@ -111,7 +111,7 @@ final class DamusVideoPlayerViewModel: ObservableObject {
} }
private func load() async { private func load() async {
if let meta = controller.metadata(for: url) { if let meta = coordinator.metadata(for: url) {
has_audio = meta.has_audio has_audio = meta.has_audio
video_size = meta.size video_size = meta.size
} else { } else {
@@ -124,7 +124,7 @@ final class DamusVideoPlayerViewModel: ObservableObject {
func did_tap_mute_button() { func did_tap_mute_button() {
is_muted.toggle() is_muted.toggle()
player.isMuted = is_muted 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) { func set_view_is_visible(_ is_visible: Bool) {

View File

@@ -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
}
}