Add recent documents to home on iOS

This commit is contained in:
Arkadiusz Fal 2022-11-18 23:39:52 +01:00
parent 8ec06b0d59
commit bc1571a746
8 changed files with 148 additions and 20 deletions

View File

@ -54,6 +54,19 @@ final class DocumentsModel: ObservableObject {
return nil return nil
} }
func recentDocuments(_ limit: Int = 10) -> [URL] {
guard let documentsDirectory else { return [] }
return Array(
contents(of: documentsDirectory)
.sorted {
((try? $0.resourceValues(forKeys: [.creationDateKey]).creationDate) ?? Date()) >
((try? $1.resourceValues(forKeys: [.creationDateKey]).creationDate) ?? Date())
}
.prefix(limit)
)
}
func isDocument(_ video: Video) -> Bool { func isDocument(_ video: Video) -> Bool {
guard video.isLocal, let url = video.localStream?.localURL, let url = replacePrivateVar(url) else { return false } guard video.isLocal, let url = video.localStream?.localURL, let url = replacePrivateVar(url) else { return false }
return isDocument(url) return isDocument(url)

View File

@ -22,10 +22,13 @@ extension Defaults.Keys {
static let enableReturnYouTubeDislike = Key<Bool>("enableReturnYouTubeDislike", default: false) static let enableReturnYouTubeDislike = Key<Bool>("enableReturnYouTubeDislike", default: false)
static let showHome = Key<Bool>("showHome", default: true) static let showHome = Key<Bool>("showHome", default: true)
static let showDocuments = Key<Bool>("showDocuments", default: true)
static let showOpenActionsInHome = Key<Bool>("showOpenActionsInHome", default: true) static let showOpenActionsInHome = Key<Bool>("showOpenActionsInHome", default: true)
static let showOpenActionsToolbarItem = Key<Bool>("showOpenActionsToolbarItem", default: false) static let showOpenActionsToolbarItem = Key<Bool>("showOpenActionsToolbarItem", default: false)
static let showFavoritesInHome = Key<Bool>("showFavoritesInHome", default: true) static let showFavoritesInHome = Key<Bool>("showFavoritesInHome", default: true)
#if os(iOS)
static let showDocuments = Key<Bool>("showDocuments", default: false)
static let homeRecentDocumentsItems = Key<Int>("homeRecentDocumentsItems", default: 3)
#endif
static let homeHistoryItems = Key<Int>("homeHistoryItems", default: 10) static let homeHistoryItems = Key<Int>("homeHistoryItems", default: 10)
static let favorites = Key<[FavoriteItem]>("favorites", default: []) static let favorites = Key<[FavoriteItem]>("favorites", default: [])

View File

@ -7,16 +7,7 @@ struct DocumentsView: View {
BrowserPlayerControls { BrowserPlayerControls {
ScrollView(.vertical, showsIndicators: false) { ScrollView(.vertical, showsIndicators: false) {
if model.directoryContents.isEmpty { if model.directoryContents.isEmpty {
VStack(alignment: .center, spacing: 20) { NoDocumentsView()
HStack {
Image(systemName: "doc")
Text("No documents")
}
Text("Share files from Finder on a Mac\nor iTunes on Windows")
.multilineTextAlignment(.center)
}
.frame(maxWidth: .infinity)
.foregroundColor(.secondary)
} else { } else {
ForEach(model.sortedDirectoryContents, id: \.absoluteString) { url in ForEach(model.sortedDirectoryContents, id: \.absoluteString) { url in
let video = Video.local(model.replacePrivateVar(url) ?? url) let video = Video.local(model.replacePrivateVar(url) ?? url)

View File

@ -0,0 +1,22 @@
import SwiftUI
struct NoDocumentsView: View {
var body: some View {
VStack(alignment: .center, spacing: 20) {
HStack {
Image(systemName: "doc")
Text("No documents")
}
Text("Share files from Finder on a Mac\nor iTunes on Windows")
.multilineTextAlignment(.center)
}
.frame(maxWidth: .infinity)
.foregroundColor(.secondary)
}
}
struct NoDocumentsView_Previews: PreviewProvider {
static var previews: some View {
NoDocumentsView()
}
}

View File

@ -0,0 +1,36 @@
import Defaults
import SwiftUI
struct RecentDocumentsView: View {
var limit = 3
let model = DocumentsModel.shared
var body: some View {
LazyVStack {
if recentDocuments.isEmpty {
NoDocumentsView()
} else {
ForEach(recentDocuments, id: \.absoluteString) { url in
let video = Video.local(model.replacePrivateVar(url) ?? url)
PlayerQueueRow(
item: PlayerQueueItem(video)
)
.contextMenu {
VideoContextMenuView(video: video)
}
}
}
}
.padding(.horizontal, 15)
}
var recentDocuments: [URL] {
model.recentDocuments(limit)
}
}
struct RecentDocumentsView_Previews: PreviewProvider {
static var previews: some View {
RecentDocumentsView()
}
}

View File

@ -15,12 +15,18 @@ struct HomeView: View {
@FetchRequest(sortDescriptors: [.init(key: "watchedAt", ascending: false)]) @FetchRequest(sortDescriptors: [.init(key: "watchedAt", ascending: false)])
var watches: FetchedResults<Watch> var watches: FetchedResults<Watch>
@State private var historyID = UUID() @State private var historyID = UUID()
#if os(iOS)
@State private var recentDocumentsID = UUID()
#endif
var favoritesObserver: Any? var favoritesObserver: Any?
#if !os(tvOS) #if !os(tvOS)
@Default(.favorites) private var favorites @Default(.favorites) private var favorites
#endif #endif
#if os(iOS)
@Default(.homeRecentDocumentsItems) private var homeRecentDocumentsItems
#endif
@Default(.homeHistoryItems) private var homeHistoryItems @Default(.homeHistoryItems) private var homeHistoryItems
@Default(.showFavoritesInHome) private var showFavoritesInHome @Default(.showFavoritesInHome) private var showFavoritesInHome
@Default(.showOpenActionsInHome) private var showOpenActionsInHome @Default(.showOpenActionsInHome) private var showOpenActionsInHome
@ -87,10 +93,33 @@ struct HomeView: View {
#endif #endif
} }
if homeRecentDocumentsItems > 0 {
VStack {
HStack {
sectionLabel("Recent Documents")
Spacer()
Button {
recentDocumentsID = UUID()
} label: {
Label("Refresh", systemImage: "arrow.clockwise")
.font(.headline)
.labelStyle(.iconOnly)
.foregroundColor(.secondary)
}
}
RecentDocumentsView(limit: homeRecentDocumentsItems)
.id(recentDocumentsID)
}
.frame(maxWidth: .infinity, alignment: .leading)
}
if homeHistoryItems > 0 { if homeHistoryItems > 0 {
VStack { VStack {
HStack { HStack {
Text("History") sectionLabel("History")
Spacer() Spacer()
Button { Button {
navigation.presentAlert( navigation.presentAlert(
@ -108,17 +137,11 @@ struct HomeView: View {
Label("Clear History", systemImage: "trash") Label("Clear History", systemImage: "trash")
.font(.headline) .font(.headline)
.labelStyle(.iconOnly) .labelStyle(.iconOnly)
.foregroundColor(.secondary)
} }
} }
#if os(tvOS)
.padding(.horizontal, 40)
#else
.padding(.horizontal, 15)
#endif
.font(.title3.bold())
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
.foregroundColor(.secondary)
HistoryView(limit: homeHistoryItems) HistoryView(limit: homeHistoryItems)
.id(historyID) .id(historyID)
@ -157,9 +180,21 @@ struct HomeView: View {
#endif #endif
} }
} }
func sectionLabel(_ label: String) -> some View {
Text(label)
#if os(tvOS)
.padding(.horizontal, 40)
#else
.padding(.horizontal, 15)
#endif
.font(.title3.bold())
.frame(maxWidth: .infinity, alignment: .leading)
.foregroundColor(.secondary)
}
} }
struct Favorites_Previews: PreviewProvider { struct Home_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
TabView { TabView {
HomeView() HomeView()

View File

@ -8,6 +8,7 @@ struct BrowsingSettings: View {
#endif #endif
@Default(.accountPickerDisplaysAnonymousAccounts) private var accountPickerDisplaysAnonymousAccounts @Default(.accountPickerDisplaysAnonymousAccounts) private var accountPickerDisplaysAnonymousAccounts
#if os(iOS) #if os(iOS)
@Default(.homeRecentDocumentsItems) private var homeRecentDocumentsItems
@Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing @Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing
#endif #endif
@Default(.thumbnailsQuality) private var thumbnailsQuality @Default(.thumbnailsQuality) private var thumbnailsQuality
@ -24,6 +25,9 @@ struct BrowsingSettings: View {
@EnvironmentObject<AccountsModel> private var accounts @EnvironmentObject<AccountsModel> private var accounts
@State private var homeHistoryItemsText = "" @State private var homeHistoryItemsText = ""
#if os(iOS)
@State private var homeRecentDocumentsItemsText = ""
#endif
#if os(macOS) #if os(macOS)
@State private var presentingEditFavoritesSheet = false @State private var presentingEditFavoritesSheet = false
#endif #endif
@ -87,6 +91,22 @@ struct BrowsingSettings: View {
} }
.multilineTextAlignment(.trailing) .multilineTextAlignment(.trailing)
HStack {
Text("Recent documents")
TextField("Recent documents", text: $homeRecentDocumentsItemsText)
.labelsHidden()
#if !os(macOS)
.keyboardType(.numberPad)
#endif
.onAppear {
homeRecentDocumentsItemsText = String(homeRecentDocumentsItems)
}
.onChange(of: homeRecentDocumentsItemsText) { newValue in
homeRecentDocumentsItems = Int(newValue) ?? 3
}
}
.multilineTextAlignment(.trailing)
if !accounts.isEmpty { if !accounts.isEmpty {
Toggle("Show Favorites", isOn: $showFavoritesInHome) Toggle("Show Favorites", isOn: $showFavoritesInHome)

View File

@ -77,6 +77,8 @@
3705B182267B4E4900704544 /* TrendingCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705B181267B4E4900704544 /* TrendingCategory.swift */; }; 3705B182267B4E4900704544 /* TrendingCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705B181267B4E4900704544 /* TrendingCategory.swift */; };
3705B183267B4E4900704544 /* TrendingCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705B181267B4E4900704544 /* TrendingCategory.swift */; }; 3705B183267B4E4900704544 /* TrendingCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705B181267B4E4900704544 /* TrendingCategory.swift */; };
3705B184267B4E4900704544 /* TrendingCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705B181267B4E4900704544 /* TrendingCategory.swift */; }; 3705B184267B4E4900704544 /* TrendingCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705B181267B4E4900704544 /* TrendingCategory.swift */; };
3709528829283A21001ECA40 /* RecentDocumentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3709528729283A21001ECA40 /* RecentDocumentsView.swift */; };
3709528A29283E14001ECA40 /* NoDocumentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3709528929283E14001ECA40 /* NoDocumentsView.swift */; };
37095E82291DC85400301883 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37095E81291DC85400301883 /* ShareViewController.swift */; }; 37095E82291DC85400301883 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37095E81291DC85400301883 /* ShareViewController.swift */; };
37095E89291DC85400301883 /* Open in Yattee.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 37095E7F291DC85400301883 /* Open in Yattee.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 37095E89291DC85400301883 /* Open in Yattee.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 37095E7F291DC85400301883 /* Open in Yattee.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
37095E8D291DD5DA00301883 /* URLBookmarkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37F5E8B5291BE9D0006C15F5 /* URLBookmarkModel.swift */; }; 37095E8D291DD5DA00301883 /* URLBookmarkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37F5E8B5291BE9D0006C15F5 /* URLBookmarkModel.swift */; };
@ -1020,6 +1022,8 @@
3703100127B0713600ECDDAA /* PlayerGestures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerGestures.swift; sourceTree = "<group>"; }; 3703100127B0713600ECDDAA /* PlayerGestures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerGestures.swift; sourceTree = "<group>"; };
3705B17F267B4DFB00704544 /* TrendingCountry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingCountry.swift; sourceTree = "<group>"; }; 3705B17F267B4DFB00704544 /* TrendingCountry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingCountry.swift; sourceTree = "<group>"; };
3705B181267B4E4900704544 /* TrendingCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingCategory.swift; sourceTree = "<group>"; }; 3705B181267B4E4900704544 /* TrendingCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingCategory.swift; sourceTree = "<group>"; };
3709528729283A21001ECA40 /* RecentDocumentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentDocumentsView.swift; sourceTree = "<group>"; };
3709528929283E14001ECA40 /* NoDocumentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoDocumentsView.swift; sourceTree = "<group>"; };
37095E7F291DC85400301883 /* Open in Yattee.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Open in Yattee.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 37095E7F291DC85400301883 /* Open in Yattee.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Open in Yattee.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
37095E81291DC85400301883 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; }; 37095E81291DC85400301883 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
37095E86291DC85400301883 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 37095E86291DC85400301883 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -1873,6 +1877,8 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
37494EA429200B14000DF176 /* DocumentsView.swift */, 37494EA429200B14000DF176 /* DocumentsView.swift */,
3709528929283E14001ECA40 /* NoDocumentsView.swift */,
3709528729283A21001ECA40 /* RecentDocumentsView.swift */,
); );
path = Documents; path = Documents;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2873,6 +2879,7 @@
375EC959289EEB8200751258 /* QualityProfileForm.swift in Sources */, 375EC959289EEB8200751258 /* QualityProfileForm.swift in Sources */,
37D2E0D028B67DBC00F64D52 /* AnimationCompletionObserverModifier.swift in Sources */, 37D2E0D028B67DBC00F64D52 /* AnimationCompletionObserverModifier.swift in Sources */,
3727B74A27872A920021C15E /* VisualEffectBlur-iOS.swift in Sources */, 3727B74A27872A920021C15E /* VisualEffectBlur-iOS.swift in Sources */,
3709528829283A21001ECA40 /* RecentDocumentsView.swift in Sources */,
37977583268922F600DD52A8 /* InvidiousAPI.swift in Sources */, 37977583268922F600DD52A8 /* InvidiousAPI.swift in Sources */,
374AB3D728BCAF0000DF56FB /* SeekModel.swift in Sources */, 374AB3D728BCAF0000DF56FB /* SeekModel.swift in Sources */,
37130A5F277657300033018A /* PersistenceController.swift in Sources */, 37130A5F277657300033018A /* PersistenceController.swift in Sources */,
@ -2916,6 +2923,7 @@
37CC3F4C270CFE1700608308 /* PlayerQueueView.swift in Sources */, 37CC3F4C270CFE1700608308 /* PlayerQueueView.swift in Sources */,
37FFC440272734C3009FFD26 /* Throttle.swift in Sources */, 37FFC440272734C3009FFD26 /* Throttle.swift in Sources */,
37DD9DB42785D58D00539416 /* RefreshControlModifier.swift in Sources */, 37DD9DB42785D58D00539416 /* RefreshControlModifier.swift in Sources */,
3709528A29283E14001ECA40 /* NoDocumentsView.swift in Sources */,
3705B182267B4E4900704544 /* TrendingCategory.swift in Sources */, 3705B182267B4E4900704544 /* TrendingCategory.swift in Sources */,
378AE940274EDFB5006A4EE1 /* Tint+Backport.swift in Sources */, 378AE940274EDFB5006A4EE1 /* Tint+Backport.swift in Sources */,
376BE50927347B5F009AD608 /* SettingsHeader.swift in Sources */, 376BE50927347B5F009AD608 /* SettingsHeader.swift in Sources */,