mirror of
https://github.com/yattee/yattee.git
synced 2025-08-04 01:34:10 +00:00
Improve open videos layouts
This commit is contained in:
@@ -10,24 +10,34 @@ struct HistoryView: View {
|
||||
|
||||
var body: some View {
|
||||
LazyVStack {
|
||||
ForEach(visibleWatches, id: \.videoID) { watch in
|
||||
PlayerQueueRow(
|
||||
item: PlayerQueueItem.from(watch, video: player.historyVideo(watch.videoID)),
|
||||
history: true
|
||||
)
|
||||
.onAppear {
|
||||
player.loadHistoryVideoDetails(watch.videoID)
|
||||
if visibleWatches.isEmpty {
|
||||
VStack(alignment: .center, spacing: 20) {
|
||||
HStack {
|
||||
Image(systemName: "clock")
|
||||
Text("Playback history is empty")
|
||||
|
||||
}.foregroundColor(.secondary)
|
||||
}
|
||||
.contextMenu {
|
||||
VideoContextMenuView(video: player.historyVideo(watch.videoID) ?? watch.video)
|
||||
} else {
|
||||
ForEach(visibleWatches, id: \.videoID) { watch in
|
||||
PlayerQueueRow(
|
||||
item: PlayerQueueItem.from(watch, video: player.historyVideo(watch.videoID)),
|
||||
history: true
|
||||
)
|
||||
.onAppear {
|
||||
player.loadHistoryVideoDetails(watch.videoID)
|
||||
}
|
||||
.contextMenu {
|
||||
VideoContextMenuView(video: player.historyVideo(watch.videoID) ?? watch.video)
|
||||
}
|
||||
}
|
||||
}
|
||||
#if os(tvOS)
|
||||
.padding(.horizontal, 40)
|
||||
#else
|
||||
.padding(.horizontal, 15)
|
||||
#endif
|
||||
}
|
||||
#if os(tvOS)
|
||||
.padding(.horizontal, 40)
|
||||
#else
|
||||
.padding(.horizontal, 15)
|
||||
#endif
|
||||
}
|
||||
|
||||
private var visibleWatches: [Watch] {
|
||||
|
@@ -24,6 +24,36 @@ struct HomeView: View {
|
||||
var body: some View {
|
||||
BrowserPlayerControls {
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
HStack {
|
||||
#if os(tvOS)
|
||||
OpenVideosButton(text: "Open Video", imageSystemName: "globe") {
|
||||
NavigationModel.shared.presentingOpenVideos = true
|
||||
}
|
||||
.frame(maxWidth: 600)
|
||||
#else
|
||||
OpenVideosButton(text: "Files", imageSystemName: "folder") {
|
||||
NavigationModel.shared.presentingFileImporter = true
|
||||
}
|
||||
OpenVideosButton(text: "Paste", imageSystemName: "doc.on.clipboard.fill") {
|
||||
OpenVideosModel.shared.openURLsFromClipboard(playbackMode: .playNow)
|
||||
}
|
||||
OpenVideosButton(imageSystemName: "ellipsis") {
|
||||
NavigationModel.shared.presentingOpenVideos = true
|
||||
}
|
||||
.frame(maxWidth: 40)
|
||||
#endif
|
||||
}
|
||||
#if os(iOS)
|
||||
.padding(.top, RefreshControl.navigationBarTitleDisplayMode == .inline ? 15 : 0)
|
||||
#else
|
||||
.padding(.top, 15)
|
||||
#endif
|
||||
#if os(tvOS)
|
||||
.padding(.horizontal, 40)
|
||||
#else
|
||||
.padding(.horizontal, 15)
|
||||
#endif
|
||||
|
||||
if !accounts.current.isNil {
|
||||
#if os(tvOS)
|
||||
ForEach(Defaults[.favorites]) { item in
|
||||
@@ -60,18 +90,7 @@ struct HomeView: View {
|
||||
HistoryView(limit: homeHistoryItems)
|
||||
}
|
||||
|
||||
#if os(tvOS)
|
||||
HStack {
|
||||
Button {
|
||||
navigation.presentingOpenVideos = true
|
||||
} label: {
|
||||
Label("Open Videos...", systemImage: "folder")
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.vertical, 10)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
#else
|
||||
#if !os(tvOS)
|
||||
Color.clear.padding(.bottom, 60)
|
||||
#endif
|
||||
}
|
||||
|
@@ -120,3 +120,10 @@ struct AppSidebarNavigation: View {
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
struct AppSidebarNavigation_Preview: PreviewProvider {
|
||||
static var previews: some View {
|
||||
AppSidebarNavigation()
|
||||
.injectFixtureEnvironmentObjects()
|
||||
}
|
||||
}
|
||||
|
@@ -85,6 +85,32 @@ struct ContentView: View {
|
||||
}
|
||||
)
|
||||
#if !os(tvOS)
|
||||
.fileImporter(
|
||||
isPresented: $navigation.presentingFileImporter,
|
||||
allowedContentTypes: [.audiovisualContent],
|
||||
allowsMultipleSelection: true
|
||||
) { result in
|
||||
do {
|
||||
let selectedFiles = try result.get()
|
||||
let urlsToOpen = selectedFiles.map { url in
|
||||
if let bookmarkURL = URLBookmarkModel.shared.loadBookmark(url) {
|
||||
return bookmarkURL
|
||||
}
|
||||
|
||||
if url.startAccessingSecurityScopedResource() {
|
||||
URLBookmarkModel.shared.saveBookmark(url)
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
OpenVideosModel.shared.openURLs(urlsToOpen)
|
||||
} catch {
|
||||
NavigationModel.shared.presentAlert(title: "Could not open Files")
|
||||
}
|
||||
|
||||
NavigationModel.shared.presentingOpenVideos = false
|
||||
}
|
||||
.onOpenURL {
|
||||
OpenURLHandler(
|
||||
accounts: accounts,
|
||||
|
39
Shared/Views/OpenVideosButton.swift
Normal file
39
Shared/Views/OpenVideosButton.swift
Normal file
@@ -0,0 +1,39 @@
|
||||
import SwiftUI
|
||||
|
||||
struct OpenVideosButton: View {
|
||||
var text: String?
|
||||
var imageSystemName: String?
|
||||
var action: () -> Void = {}
|
||||
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
HStack {
|
||||
if let imageSystemName {
|
||||
Image(systemName: imageSystemName)
|
||||
}
|
||||
if let text {
|
||||
Text(text ?? "")
|
||||
.fontWeight(.bold)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.frame(minHeight: 45)
|
||||
.frame(maxWidth: .infinity)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.foregroundColor(.accentColor)
|
||||
.buttonStyle(.plain)
|
||||
.background(buttonBackground)
|
||||
}
|
||||
|
||||
var buttonBackground: some View {
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.foregroundColor(Color.accentColor.opacity(0.33))
|
||||
}
|
||||
}
|
||||
|
||||
struct OpenVideosButton_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
OpenVideosButton(text: "Open Videos", imageSystemName: "play.circle.fill")
|
||||
}
|
||||
}
|
@@ -18,7 +18,7 @@ struct OpenVideosView: View {
|
||||
var body: some View {
|
||||
#if os(macOS)
|
||||
openVideos
|
||||
.frame(minWidth: 600, maxWidth: 800, minHeight: 250)
|
||||
.frame(minWidth: 600, maxWidth: 800, minHeight: 350, maxHeight: 500)
|
||||
#else
|
||||
NavigationView {
|
||||
openVideos
|
||||
@@ -44,48 +44,45 @@ struct OpenVideosView: View {
|
||||
VStack(alignment: .leading) {
|
||||
ZStack(alignment: .topLeading) {
|
||||
#if os(tvOS)
|
||||
TextField("URLs to Open", text: $urlsToOpenText)
|
||||
TextField("URL to Open", text: $urlsToOpenText)
|
||||
#else
|
||||
TextEditor(text: $urlsToOpenText)
|
||||
.padding(2)
|
||||
.border(Color(white: 0.8), width: 1)
|
||||
.frame(minHeight: 100, maxHeight: 200)
|
||||
.frame(minHeight: 100, maxHeight: 250)
|
||||
#if !os(macOS)
|
||||
.keyboardType(.URL)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
Text("Enter or paste URLs to open, one per line")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Menu {
|
||||
Picker("Playback Mode", selection: $playbackMode) {
|
||||
ForEach(OpenVideosModel.PlaybackMode.allCases, id: \.rawValue) { mode in
|
||||
Text(mode.description).tag(mode)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text(playbackMode.description)
|
||||
Group {
|
||||
#if os(tvOS)
|
||||
Text("Enter link to open")
|
||||
#else
|
||||
Text("Enter links to open, one per line")
|
||||
#endif
|
||||
}
|
||||
.transaction { t in t.disablesAnimations = true }
|
||||
.labelsHidden()
|
||||
.padding(.bottom, 5)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
playbackModeControl
|
||||
|
||||
Toggle(isOn: $removeQueueItems) {
|
||||
Text("Clear queue before opening")
|
||||
Text("Clear Queue before opening")
|
||||
}
|
||||
.disabled(!playbackMode.allowsRemovingQueueItems)
|
||||
.padding(.bottom)
|
||||
|
||||
HStack {
|
||||
Group {
|
||||
#if os(tvOS)
|
||||
Spacer()
|
||||
#endif
|
||||
openURLsButton
|
||||
Spacer()
|
||||
|
||||
#if !os(tvOS)
|
||||
Spacer()
|
||||
|
||||
openFromClipboardButton
|
||||
#endif
|
||||
@@ -100,6 +97,7 @@ struct OpenVideosView: View {
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
.alert(isPresented: $navigation.presentingAlertInOpenVideos) { navigation.alert }
|
||||
#if !os(tvOS)
|
||||
.fileImporter(
|
||||
isPresented: $presentingFileImporter,
|
||||
@@ -122,7 +120,8 @@ struct OpenVideosView: View {
|
||||
|
||||
openURLs(selectedFiles)
|
||||
} catch {
|
||||
NavigationModel.shared.presentAlert(title: "Could not open Files")
|
||||
NavigationModel.shared.alert = Alert(title: Text("Could not open Files"))
|
||||
NavigationModel.shared.presentingAlertInOpenVideos = true
|
||||
}
|
||||
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
@@ -130,77 +129,64 @@ struct OpenVideosView: View {
|
||||
#endif
|
||||
}
|
||||
|
||||
var openURLsButton: some View {
|
||||
Button {
|
||||
openURLs(urlsToOpenFromText)
|
||||
} label: {
|
||||
HStack {
|
||||
Image(systemName: "network")
|
||||
Text("Open")
|
||||
.fontWeight(.bold)
|
||||
.padding(.vertical, 10)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.contentShape(Rectangle())
|
||||
.padding(.horizontal, 20)
|
||||
var playbackModeControl: some View {
|
||||
HStack {
|
||||
#if !os(tvOS)
|
||||
Text("Playback Mode")
|
||||
Spacer()
|
||||
#endif
|
||||
#if os(iOS)
|
||||
Menu {
|
||||
playbackModePicker
|
||||
} label: {
|
||||
Text(playbackMode.description)
|
||||
}
|
||||
#else
|
||||
playbackModePicker
|
||||
#if !os(tvOS)
|
||||
.frame(maxWidth: 200)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
.transaction { t in t.animation = .none }
|
||||
.padding(.bottom, 5)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
}
|
||||
|
||||
var playbackModePicker: some View {
|
||||
Picker("Playback Mode", selection: $playbackMode) {
|
||||
ForEach(OpenVideosModel.PlaybackMode.allCases, id: \.rawValue) { mode in
|
||||
Text(mode.description).tag(mode)
|
||||
}
|
||||
}
|
||||
.labelsHidden()
|
||||
}
|
||||
|
||||
var openURLsButton: some View {
|
||||
OpenVideosButton(text: "Open", imageSystemName: "network") {
|
||||
openURLs(urlsToOpenFromText)
|
||||
}
|
||||
.foregroundColor(.accentColor)
|
||||
.buttonStyle(.plain)
|
||||
.background(buttonBackground)
|
||||
.disabled(urlsToOpenFromText.isEmpty)
|
||||
#if !os(tvOS)
|
||||
#if os(tvOS)
|
||||
.frame(maxWidth: 600)
|
||||
#else
|
||||
.keyboardShortcut(.defaultAction)
|
||||
#endif
|
||||
}
|
||||
|
||||
var openFromClipboardButton: some View {
|
||||
Button {
|
||||
OpenVideosButton(text: "Paste", imageSystemName: "doc.on.clipboard.fill") {
|
||||
OpenVideosModel.shared.openURLsFromClipboard(
|
||||
removeQueueItems: removeQueueItems,
|
||||
playbackMode: playbackMode
|
||||
)
|
||||
} label: {
|
||||
HStack {
|
||||
Image(systemName: "doc.on.clipboard.fill")
|
||||
Text("Paste")
|
||||
.fontWeight(.bold)
|
||||
.padding(.vertical, 10)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.contentShape(Rectangle())
|
||||
.padding(.horizontal, 20)
|
||||
}
|
||||
.foregroundColor(.accentColor)
|
||||
.buttonStyle(.plain)
|
||||
.background(buttonBackground)
|
||||
#if !os(tvOS)
|
||||
.keyboardShortcut(.defaultAction)
|
||||
#endif
|
||||
}
|
||||
|
||||
var openFilesButton: some View {
|
||||
Button {
|
||||
OpenVideosButton(text: "Open Files", imageSystemName: "folder") {
|
||||
presentingFileImporter = true
|
||||
} label: {
|
||||
HStack {
|
||||
Image(systemName: "folder")
|
||||
Text("Open Files")
|
||||
|
||||
.fontWeight(.bold)
|
||||
.padding(.vertical, 10)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.contentShape(Rectangle())
|
||||
.padding(.horizontal, 20)
|
||||
}
|
||||
.foregroundColor(.accentColor)
|
||||
.buttonStyle(.plain)
|
||||
.background(buttonBackground)
|
||||
}
|
||||
|
||||
var buttonBackground: some View {
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.foregroundColor(Color.accentColor.opacity(0.33))
|
||||
}
|
||||
|
||||
var urlsToOpenFromText: [URL] {
|
||||
@@ -217,6 +203,7 @@ struct OpenVideosView: View {
|
||||
struct OpenVideosView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
OpenVideosView()
|
||||
.injectFixtureEnvironmentObjects()
|
||||
#if os(iOS)
|
||||
.navigationViewStyle(.stack)
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user