mirror of
https://github.com/yattee/yattee.git
synced 2025-10-12 18:38:16 +00:00
Watch Next behavior and settings
This commit is contained in:
@@ -14,15 +14,36 @@ struct WatchNextView: View {
|
||||
#if os(iOS)
|
||||
NavigationView {
|
||||
watchNext
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
watchNextMenu
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
VStack {
|
||||
HStack {
|
||||
closeButton
|
||||
hideCloseButton
|
||||
.labelStyle(.iconOnly)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Spacer()
|
||||
reopenButton
|
||||
|
||||
watchNextMenu
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack {
|
||||
if model.isRestartable {
|
||||
reopenButton
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||
}
|
||||
#if os(macOS)
|
||||
.padding()
|
||||
#endif
|
||||
watchNext
|
||||
}
|
||||
#endif
|
||||
@@ -32,143 +53,215 @@ struct WatchNextView: View {
|
||||
#else
|
||||
.background(Color.background)
|
||||
#endif
|
||||
.opacity(model.presentingOutro ? 1 : 0)
|
||||
.opacity(model.isPresenting ? 1 : 0)
|
||||
}
|
||||
|
||||
var watchNext: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
if model.isAutoplaying,
|
||||
let item = nextFromTheQueue
|
||||
let item = model.nextFromTheQueue
|
||||
{
|
||||
HStack {
|
||||
Text("Playing Next in 5...")
|
||||
.font(.headline)
|
||||
Text("Playing Next in \(Int(model.countdown.rounded()))...")
|
||||
.font(.headline.monospacedDigit())
|
||||
Spacer()
|
||||
|
||||
Button {
|
||||
model.cancelAutoplay()
|
||||
model.keepFromAutoplaying()
|
||||
} label: {
|
||||
Label("Cancel", systemImage: "xmark")
|
||||
Label("Cancel", systemImage: "pause.fill")
|
||||
#if os(iOS)
|
||||
.imageScale(.large)
|
||||
.padding([.vertical, .leading])
|
||||
.font(.headline.bold())
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if os(tvOS)
|
||||
.padding(.top, 10)
|
||||
#endif
|
||||
|
||||
PlayerQueueRow(item: item)
|
||||
.padding(.bottom, 10)
|
||||
}
|
||||
|
||||
moreVideos
|
||||
.padding(.top, 15)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
#endif
|
||||
.navigationTitle("Watch Next")
|
||||
#if !os(macOS)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
closeButton
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
reopenButton
|
||||
}
|
||||
.navigationTitle(model.page.title)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
hideCloseButton
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
reopenButton
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
var watchNextMenu: some View {
|
||||
#if os(tvOS)
|
||||
Button {
|
||||
model.page = model.page.next()
|
||||
} label: {
|
||||
menuLabel
|
||||
}
|
||||
#elseif os(macOS)
|
||||
pagePicker
|
||||
.modifier(SettingsPickerModifier())
|
||||
#if os(macOS)
|
||||
.frame(maxWidth: 150)
|
||||
#endif
|
||||
#else
|
||||
Menu {
|
||||
pagePicker
|
||||
} label: {
|
||||
HStack(spacing: 12) {
|
||||
menuLabel
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Image(systemName: "chevron.down.circle.fill")
|
||||
.foregroundColor(.accentColor)
|
||||
.imageScale(.small)
|
||||
}
|
||||
.transaction { t in t.animation = nil }
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
var menuLabel: some View {
|
||||
HStack {
|
||||
Image(systemName: model.page.systemImageName)
|
||||
.imageScale(.small)
|
||||
Text(model.page.title)
|
||||
.font(.headline)
|
||||
}
|
||||
}
|
||||
|
||||
var pagePicker: some View {
|
||||
Picker("Page", selection: $model.page) {
|
||||
ForEach(WatchNextViewModel.Page.allCases, id: \.rawValue) { page in
|
||||
Label(page.title, systemImage: page.systemImageName)
|
||||
.tag(page)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder var hideCloseButton: some View {
|
||||
if model.isHideable {
|
||||
hideButton
|
||||
} else {
|
||||
closeButton
|
||||
}
|
||||
}
|
||||
|
||||
var hideButton: some View {
|
||||
Button {
|
||||
model.hide()
|
||||
} label: {
|
||||
Label("Hide", systemImage: "chevron.down")
|
||||
}
|
||||
}
|
||||
|
||||
var closeButton: some View {
|
||||
Button {
|
||||
player.closeCurrentItem()
|
||||
player.hide()
|
||||
Delay.by(0.8) {
|
||||
model.presentingOutro = false
|
||||
}
|
||||
model.close()
|
||||
} label: {
|
||||
Label("Close", systemImage: "xmark")
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder var reopenButton: some View {
|
||||
if player.currentItem != nil, model.item != nil {
|
||||
if model.isRestartable {
|
||||
Button {
|
||||
model.close()
|
||||
model.restart()
|
||||
} label: {
|
||||
Label("Back to last video", systemImage: "arrow.counterclockwise")
|
||||
Label(model.reason == .userInteracted ? "Back" : "Reopen", systemImage: "arrow.counterclockwise")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder var moreVideos: some View {
|
||||
VStack(spacing: 12) {
|
||||
let queueForMoreVideos = player.queue.isEmpty ? [] : player.queue.suffix(from: model.isAutoplaying ? 1 : 0)
|
||||
if !queueForMoreVideos.isEmpty {
|
||||
VStack(spacing: 12) {
|
||||
Text("Next in Queue")
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.font(.headline)
|
||||
|
||||
switch model.page {
|
||||
case .queue:
|
||||
let queueForMoreVideos = player.queue.isEmpty ? [] : player.queue.suffix(from: model.isAutoplaying ? 1 : 0)
|
||||
if !queueForMoreVideos.isEmpty {
|
||||
ForEach(queueForMoreVideos) { item in
|
||||
ContentItemView(item: .init(video: item.video))
|
||||
.environment(\.listingStyle, .list)
|
||||
PlayerQueueRow(item: item)
|
||||
.contextMenu {
|
||||
removeButton(item)
|
||||
removeAllButton()
|
||||
|
||||
if let video = item.video {
|
||||
VideoContextMenuView(video: video)
|
||||
}
|
||||
}
|
||||
#if os(tvOS)
|
||||
.padding(.horizontal, 30)
|
||||
#endif
|
||||
|
||||
#if !os(tvOS)
|
||||
Divider()
|
||||
#endif
|
||||
}
|
||||
} else if player.playbackMode != .related && player.playbackMode != .loopOne {
|
||||
Label(
|
||||
model.isAutoplaying ? "Nothing more in the queue" : "Queue is empty",
|
||||
systemImage: WatchNextViewModel.Page.queue.systemImageName
|
||||
)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
if let item = model.item {
|
||||
VStack(spacing: 12) {
|
||||
Text("Related videos")
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.font(.headline)
|
||||
|
||||
case .related:
|
||||
if let item = model.item {
|
||||
ForEach(item.video.related) { video in
|
||||
ContentItemView(item: .init(video: video))
|
||||
.environment(\.listingStyle, .list)
|
||||
}
|
||||
.padding(.bottom, 4)
|
||||
} else {
|
||||
Label("Nothing was played",
|
||||
systemImage: WatchNextViewModel.Page.related.systemImageName)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
if saveHistory {
|
||||
VStack(spacing: 12) {
|
||||
Text("History")
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.font(.headline)
|
||||
|
||||
HStack {
|
||||
Text("Playing Next in 5...")
|
||||
.font(.headline)
|
||||
Spacer()
|
||||
|
||||
Button {
|
||||
model.cancelAutoplay()
|
||||
} label: {
|
||||
Label("Cancel", systemImage: "pause.fill")
|
||||
}
|
||||
}
|
||||
|
||||
case .history:
|
||||
if saveHistory {
|
||||
HistoryView(limit: 15)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var nextFromTheQueue: PlayerQueueItem? {
|
||||
if player.playbackMode == .related {
|
||||
return player.autoplayItem
|
||||
} else if player.playbackMode == .queue {
|
||||
return player.queue.first
|
||||
private func removeButton(_ item: PlayerQueueItem) -> some View {
|
||||
Button {
|
||||
player.remove(item)
|
||||
} label: {
|
||||
Label("Remove from the queue", systemImage: "trash")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
private func removeAllButton() -> some View {
|
||||
Button {
|
||||
player.removeQueueItems()
|
||||
} label: {
|
||||
Label("Clear the queue", systemImage: "trash.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct OutroView_Previews: PreviewProvider {
|
||||
struct WatchNextView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
WatchNextView()
|
||||
.onAppear {
|
||||
WatchNextViewModel.shared.prepareForNextItem(.init(.fixture))
|
||||
WatchNextViewModel.shared.finishedWatching(.init(.fixture))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user