2022-11-13 17:52:15 +00:00
|
|
|
import Defaults
|
|
|
|
import SwiftUI
|
|
|
|
|
|
|
|
struct VideoActions: View {
|
2022-12-19 10:29:18 +00:00
|
|
|
enum Action: String, CaseIterable {
|
|
|
|
case share
|
|
|
|
case addToPlaylist
|
|
|
|
case subscribe
|
2023-04-22 14:33:08 +00:00
|
|
|
case fullScreen
|
|
|
|
case pip
|
|
|
|
#if os(iOS)
|
|
|
|
case lockOrientation
|
|
|
|
#endif
|
|
|
|
case restart
|
|
|
|
case advanceToNextItem
|
|
|
|
case musicMode
|
2022-12-19 10:29:18 +00:00
|
|
|
case settings
|
|
|
|
case hide
|
|
|
|
case close
|
|
|
|
}
|
|
|
|
|
2022-11-24 20:36:05 +00:00
|
|
|
@ObservedObject private var accounts = AccountsModel.shared
|
|
|
|
var navigation = NavigationModel.shared
|
2022-12-11 15:15:42 +00:00
|
|
|
@ObservedObject private var subscriptions = SubscribedChannelsModel.shared
|
2022-11-24 20:36:05 +00:00
|
|
|
@ObservedObject private var player = PlayerModel.shared
|
2022-11-13 17:52:15 +00:00
|
|
|
|
|
|
|
var video: Video?
|
|
|
|
|
2022-11-18 22:04:49 +00:00
|
|
|
@Default(.playerActionsButtonLabelStyle) private var playerActionsButtonLabelStyle
|
|
|
|
|
2022-12-19 10:29:18 +00:00
|
|
|
@Default(.actionButtonShareEnabled) private var actionButtonShareEnabled
|
|
|
|
@Default(.actionButtonAddToPlaylistEnabled) private var actionButtonAddToPlaylistEnabled
|
|
|
|
@Default(.actionButtonSubscribeEnabled) private var actionButtonSubscribeEnabled
|
|
|
|
@Default(.actionButtonSettingsEnabled) private var actionButtonSettingsEnabled
|
2023-04-22 14:33:08 +00:00
|
|
|
@Default(.actionButtonFullScreenEnabled) private var actionButtonFullScreenEnabled
|
|
|
|
@Default(.actionButtonPipEnabled) private var actionButtonPipEnabled
|
|
|
|
@Default(.actionButtonLockOrientationEnabled) private var actionButtonLockOrientationEnabled
|
|
|
|
@Default(.actionButtonRestartEnabled) private var actionButtonRestartEnabled
|
|
|
|
@Default(.actionButtonAdvanceToNextItemEnabled) private var actionButtonAdvanceToNextItemEnabled
|
|
|
|
@Default(.actionButtonMusicModeEnabled) private var actionButtonMusicModeEnabled
|
2022-12-19 10:29:18 +00:00
|
|
|
@Default(.actionButtonHideEnabled) private var actionButtonHideEnabled
|
|
|
|
@Default(.actionButtonCloseEnabled) private var actionButtonCloseEnabled
|
|
|
|
|
2022-11-13 17:52:15 +00:00
|
|
|
var body: some View {
|
2022-12-18 18:39:03 +00:00
|
|
|
HStack(spacing: 6) {
|
2022-12-19 10:29:18 +00:00
|
|
|
ForEach(Action.allCases, id: \.self) { action in
|
|
|
|
actionBody(action)
|
|
|
|
.frame(maxWidth: .infinity)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.multilineTextAlignment(.center)
|
|
|
|
.frame(maxWidth: .infinity)
|
|
|
|
.frame(height: 50)
|
|
|
|
.borderBottom(height: 0.5, color: Color("ControlsBorderColor"))
|
|
|
|
.foregroundColor(.accentColor)
|
|
|
|
}
|
|
|
|
|
|
|
|
func isVisible(_ action: Action) -> Bool {
|
|
|
|
switch action {
|
|
|
|
case .share:
|
|
|
|
return actionButtonShareEnabled
|
|
|
|
case .addToPlaylist:
|
|
|
|
return actionButtonAddToPlaylistEnabled
|
|
|
|
case .subscribe:
|
|
|
|
return actionButtonSubscribeEnabled
|
|
|
|
case .settings:
|
|
|
|
return actionButtonSettingsEnabled
|
2023-04-22 14:33:08 +00:00
|
|
|
case .fullScreen:
|
|
|
|
return actionButtonFullScreenEnabled
|
|
|
|
case .pip:
|
|
|
|
return actionButtonPipEnabled
|
|
|
|
#if os(iOS)
|
|
|
|
case .lockOrientation:
|
|
|
|
return actionButtonLockOrientationEnabled
|
|
|
|
#endif
|
|
|
|
case .restart:
|
|
|
|
return actionButtonRestartEnabled
|
|
|
|
case .advanceToNextItem:
|
|
|
|
return actionButtonAdvanceToNextItemEnabled
|
|
|
|
case .musicMode:
|
|
|
|
return actionButtonMusicModeEnabled
|
2022-12-19 10:29:18 +00:00
|
|
|
case .hide:
|
|
|
|
return actionButtonHideEnabled
|
|
|
|
case .close:
|
|
|
|
return actionButtonCloseEnabled
|
|
|
|
}
|
|
|
|
}
|
2022-11-13 17:52:15 +00:00
|
|
|
|
2022-12-19 10:29:18 +00:00
|
|
|
func isActionable(_ action: Action) -> Bool {
|
|
|
|
switch action {
|
|
|
|
case .share:
|
|
|
|
return video?.isShareable ?? false
|
|
|
|
case .addToPlaylist:
|
2023-05-21 14:35:54 +00:00
|
|
|
return !(video?.isLocal ?? true) && accounts.signedIn
|
2022-12-19 10:29:18 +00:00
|
|
|
case .subscribe:
|
|
|
|
return !(video?.isLocal ?? true) && accounts.signedIn && accounts.app.supportsSubscriptions
|
|
|
|
case .settings:
|
|
|
|
return video != nil
|
2023-04-24 10:09:22 +00:00
|
|
|
case .fullScreen:
|
|
|
|
return video != nil
|
|
|
|
case .pip:
|
|
|
|
return video != nil
|
2023-04-22 14:33:08 +00:00
|
|
|
case .advanceToNextItem:
|
|
|
|
return player.isAdvanceToNextItemAvailable
|
2023-04-24 10:09:22 +00:00
|
|
|
case .restart:
|
|
|
|
return video != nil
|
|
|
|
case .musicMode:
|
|
|
|
return video != nil
|
2022-12-19 10:29:18 +00:00
|
|
|
default:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@ViewBuilder func actionBody(_ action: Action) -> some View {
|
|
|
|
if isVisible(action) {
|
|
|
|
Group {
|
|
|
|
switch action {
|
|
|
|
case .share:
|
2022-12-19 12:35:37 +00:00
|
|
|
#if os(tvOS)
|
|
|
|
EmptyView()
|
|
|
|
#else
|
|
|
|
ShareButton(contentItem: .init(video: video)) {
|
|
|
|
actionButton("Share", systemImage: "square.and.arrow.up")
|
|
|
|
}
|
|
|
|
#endif
|
2022-12-19 10:29:18 +00:00
|
|
|
case .addToPlaylist:
|
|
|
|
actionButton("Add", systemImage: "text.badge.plus") {
|
|
|
|
guard let video else { return }
|
|
|
|
navigation.presentAddToPlaylist(video)
|
2022-11-13 22:40:18 +00:00
|
|
|
}
|
2022-12-19 10:29:18 +00:00
|
|
|
case .subscribe:
|
|
|
|
if let channel = video?.channel,
|
|
|
|
subscriptions.isSubscribing(channel.id)
|
|
|
|
{
|
|
|
|
actionButton("Unsubscribe", systemImage: "xmark.circle") {
|
|
|
|
#if os(tvOS)
|
|
|
|
subscriptions.unsubscribe(channel.id)
|
|
|
|
#else
|
|
|
|
navigation.presentUnsubscribeAlert(channel, subscriptions: subscriptions)
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
actionButton("Subscribe", systemImage: "star.circle") {
|
|
|
|
guard let video else { return }
|
2022-11-13 20:55:19 +00:00
|
|
|
|
2022-12-19 10:29:18 +00:00
|
|
|
subscriptions.subscribe(video.channel.id) {
|
|
|
|
navigation.sidebarSectionChanged.toggle()
|
|
|
|
}
|
2022-11-13 17:52:15 +00:00
|
|
|
}
|
2022-11-13 20:55:19 +00:00
|
|
|
}
|
2023-04-22 14:33:08 +00:00
|
|
|
case .fullScreen:
|
|
|
|
actionButton("Fullscreen", systemImage: player.fullscreenImage, action: player.toggleFullScreenAction)
|
|
|
|
case .pip:
|
|
|
|
actionButton("PiP", systemImage: player.pipImage, action: player.togglePiPAction)
|
|
|
|
#if os(iOS)
|
|
|
|
case .lockOrientation:
|
|
|
|
actionButton("Lock", systemImage: player.lockOrientationImage, active: player.lockedOrientation != nil, action: player.lockOrientationAction)
|
|
|
|
#endif
|
|
|
|
case .restart:
|
|
|
|
actionButton("Replay", systemImage: "backward.end.fill", action: player.replayAction)
|
|
|
|
case .advanceToNextItem:
|
|
|
|
actionButton("Next", systemImage: "forward.fill") {
|
|
|
|
player.advanceToNextItem()
|
|
|
|
}
|
|
|
|
case .musicMode:
|
|
|
|
actionButton("Music", systemImage: "music.note", active: player.musicMode, action: player.toggleMusicMode)
|
2022-12-19 10:29:18 +00:00
|
|
|
case .settings:
|
|
|
|
actionButton("Settings", systemImage: "gear") {
|
|
|
|
withAnimation(ControlOverlaysModel.animation) {
|
2022-12-21 20:16:47 +00:00
|
|
|
#if os(tvOS)
|
|
|
|
ControlOverlaysModel.shared.show()
|
|
|
|
#else
|
|
|
|
navigation.presentingPlaybackSettings = true
|
|
|
|
#endif
|
2022-12-19 10:29:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case .hide:
|
|
|
|
actionButton("Hide", systemImage: "chevron.down") {
|
|
|
|
player.hide(animate: true)
|
|
|
|
}
|
|
|
|
case .close:
|
|
|
|
actionButton("Close", systemImage: "xmark") {
|
2023-04-22 11:56:25 +00:00
|
|
|
player.closeCurrentItem()
|
2022-11-13 17:52:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-12-19 10:29:18 +00:00
|
|
|
.disabled(!isActionable(action))
|
|
|
|
}
|
|
|
|
}
|
2022-11-13 17:52:15 +00:00
|
|
|
|
|
|
|
func actionButton(
|
|
|
|
_ name: String,
|
|
|
|
systemImage: String,
|
2023-04-22 14:33:08 +00:00
|
|
|
active: Bool = false,
|
2022-11-13 17:52:15 +00:00
|
|
|
action: @escaping () -> Void = {}
|
|
|
|
) -> some View {
|
|
|
|
Button(action: action) {
|
|
|
|
VStack(spacing: 3) {
|
|
|
|
Image(systemName: systemImage)
|
|
|
|
.frame(width: 20, height: 20)
|
2024-08-27 20:02:00 +00:00
|
|
|
.foregroundColor(active ? Color("AppRedColor") : .primary)
|
2022-11-18 22:04:49 +00:00
|
|
|
if playerActionsButtonLabelStyle.text {
|
2022-11-18 23:06:13 +00:00
|
|
|
Text(name.localized())
|
2024-08-27 20:02:00 +00:00
|
|
|
.foregroundColor(active ? Color("AppRedColor") : .primary)
|
2022-11-18 22:04:49 +00:00
|
|
|
.font(.caption2)
|
|
|
|
.allowsTightening(true)
|
2023-05-21 09:50:07 +00:00
|
|
|
.lineLimit(1)
|
2022-11-18 22:04:49 +00:00
|
|
|
}
|
2022-11-13 17:52:15 +00:00
|
|
|
}
|
2022-11-18 22:04:49 +00:00
|
|
|
.padding(.horizontal, playerActionsButtonLabelStyle.text ? 6 : 12)
|
|
|
|
.padding(.vertical, playerActionsButtonLabelStyle.text ? 5 : 10)
|
2022-11-13 17:52:15 +00:00
|
|
|
.contentShape(Rectangle())
|
|
|
|
}
|
|
|
|
.buttonStyle(.plain)
|
|
|
|
.accessibilityLabel(Text(name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct VideoActions_Previews: PreviewProvider {
|
|
|
|
static var previews: some View {
|
|
|
|
VideoActions()
|
|
|
|
.injectFixtureEnvironmentObjects()
|
|
|
|
}
|
|
|
|
}
|