From bc1571a7465f0d76a3ad19a741445fd2a9625574 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Fri, 18 Nov 2022 23:39:52 +0100 Subject: [PATCH] Add recent documents to home on iOS --- Model/DocumentsModel.swift | 13 ++++++ Shared/Defaults.swift | 5 +- Shared/Documents/DocumentsView.swift | 11 +---- Shared/Documents/NoDocumentsView.swift | 22 +++++++++ Shared/Documents/RecentDocumentsView.swift | 36 +++++++++++++++ Shared/Home/HomeView.swift | 53 ++++++++++++++++++---- Shared/Settings/BrowsingSettings.swift | 20 ++++++++ Yattee.xcodeproj/project.pbxproj | 8 ++++ 8 files changed, 148 insertions(+), 20 deletions(-) create mode 100644 Shared/Documents/NoDocumentsView.swift create mode 100644 Shared/Documents/RecentDocumentsView.swift diff --git a/Model/DocumentsModel.swift b/Model/DocumentsModel.swift index 00dd38bd..171c5466 100644 --- a/Model/DocumentsModel.swift +++ b/Model/DocumentsModel.swift @@ -54,6 +54,19 @@ final class DocumentsModel: ObservableObject { 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 { guard video.isLocal, let url = video.localStream?.localURL, let url = replacePrivateVar(url) else { return false } return isDocument(url) diff --git a/Shared/Defaults.swift b/Shared/Defaults.swift index f6347413..2b8c65f7 100644 --- a/Shared/Defaults.swift +++ b/Shared/Defaults.swift @@ -22,10 +22,13 @@ extension Defaults.Keys { static let enableReturnYouTubeDislike = Key("enableReturnYouTubeDislike", default: false) static let showHome = Key("showHome", default: true) - static let showDocuments = Key("showDocuments", default: true) static let showOpenActionsInHome = Key("showOpenActionsInHome", default: true) static let showOpenActionsToolbarItem = Key("showOpenActionsToolbarItem", default: false) static let showFavoritesInHome = Key("showFavoritesInHome", default: true) + #if os(iOS) + static let showDocuments = Key("showDocuments", default: false) + static let homeRecentDocumentsItems = Key("homeRecentDocumentsItems", default: 3) + #endif static let homeHistoryItems = Key("homeHistoryItems", default: 10) static let favorites = Key<[FavoriteItem]>("favorites", default: []) diff --git a/Shared/Documents/DocumentsView.swift b/Shared/Documents/DocumentsView.swift index 062a880d..caa0137a 100644 --- a/Shared/Documents/DocumentsView.swift +++ b/Shared/Documents/DocumentsView.swift @@ -7,16 +7,7 @@ struct DocumentsView: View { BrowserPlayerControls { ScrollView(.vertical, showsIndicators: false) { if model.directoryContents.isEmpty { - 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) + NoDocumentsView() } else { ForEach(model.sortedDirectoryContents, id: \.absoluteString) { url in let video = Video.local(model.replacePrivateVar(url) ?? url) diff --git a/Shared/Documents/NoDocumentsView.swift b/Shared/Documents/NoDocumentsView.swift new file mode 100644 index 00000000..a78c80fd --- /dev/null +++ b/Shared/Documents/NoDocumentsView.swift @@ -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() + } +} diff --git a/Shared/Documents/RecentDocumentsView.swift b/Shared/Documents/RecentDocumentsView.swift new file mode 100644 index 00000000..7c50611e --- /dev/null +++ b/Shared/Documents/RecentDocumentsView.swift @@ -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() + } +} diff --git a/Shared/Home/HomeView.swift b/Shared/Home/HomeView.swift index 5ff19e0a..ab3723cb 100644 --- a/Shared/Home/HomeView.swift +++ b/Shared/Home/HomeView.swift @@ -15,12 +15,18 @@ struct HomeView: View { @FetchRequest(sortDescriptors: [.init(key: "watchedAt", ascending: false)]) var watches: FetchedResults @State private var historyID = UUID() + #if os(iOS) + @State private var recentDocumentsID = UUID() + #endif var favoritesObserver: Any? #if !os(tvOS) @Default(.favorites) private var favorites #endif + #if os(iOS) + @Default(.homeRecentDocumentsItems) private var homeRecentDocumentsItems + #endif @Default(.homeHistoryItems) private var homeHistoryItems @Default(.showFavoritesInHome) private var showFavoritesInHome @Default(.showOpenActionsInHome) private var showOpenActionsInHome @@ -87,10 +93,33 @@ struct HomeView: View { #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 { VStack { HStack { - Text("History") + sectionLabel("History") Spacer() Button { navigation.presentAlert( @@ -108,17 +137,11 @@ struct HomeView: View { Label("Clear History", systemImage: "trash") .font(.headline) .labelStyle(.iconOnly) + .foregroundColor(.secondary) } } - #if os(tvOS) - .padding(.horizontal, 40) - #else - .padding(.horizontal, 15) - #endif - .font(.title3.bold()) .frame(maxWidth: .infinity, alignment: .leading) - .foregroundColor(.secondary) HistoryView(limit: homeHistoryItems) .id(historyID) @@ -157,9 +180,21 @@ struct HomeView: View { #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 { TabView { HomeView() diff --git a/Shared/Settings/BrowsingSettings.swift b/Shared/Settings/BrowsingSettings.swift index 224e5d08..cc1348e4 100644 --- a/Shared/Settings/BrowsingSettings.swift +++ b/Shared/Settings/BrowsingSettings.swift @@ -8,6 +8,7 @@ struct BrowsingSettings: View { #endif @Default(.accountPickerDisplaysAnonymousAccounts) private var accountPickerDisplaysAnonymousAccounts #if os(iOS) + @Default(.homeRecentDocumentsItems) private var homeRecentDocumentsItems @Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing #endif @Default(.thumbnailsQuality) private var thumbnailsQuality @@ -24,6 +25,9 @@ struct BrowsingSettings: View { @EnvironmentObject private var accounts @State private var homeHistoryItemsText = "" + #if os(iOS) + @State private var homeRecentDocumentsItemsText = "" + #endif #if os(macOS) @State private var presentingEditFavoritesSheet = false #endif @@ -87,6 +91,22 @@ struct BrowsingSettings: View { } .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 { Toggle("Show Favorites", isOn: $showFavoritesInHome) diff --git a/Yattee.xcodeproj/project.pbxproj b/Yattee.xcodeproj/project.pbxproj index 54caeb06..3390d4b1 100644 --- a/Yattee.xcodeproj/project.pbxproj +++ b/Yattee.xcodeproj/project.pbxproj @@ -77,6 +77,8 @@ 3705B182267B4E4900704544 /* 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 */; }; + 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 */; }; 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 */; }; @@ -1020,6 +1022,8 @@ 3703100127B0713600ECDDAA /* PlayerGestures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerGestures.swift; sourceTree = ""; }; 3705B17F267B4DFB00704544 /* TrendingCountry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingCountry.swift; sourceTree = ""; }; 3705B181267B4E4900704544 /* TrendingCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingCategory.swift; sourceTree = ""; }; + 3709528729283A21001ECA40 /* RecentDocumentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentDocumentsView.swift; sourceTree = ""; }; + 3709528929283E14001ECA40 /* NoDocumentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoDocumentsView.swift; sourceTree = ""; }; 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 = ""; }; 37095E86291DC85400301883 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1873,6 +1877,8 @@ isa = PBXGroup; children = ( 37494EA429200B14000DF176 /* DocumentsView.swift */, + 3709528929283E14001ECA40 /* NoDocumentsView.swift */, + 3709528729283A21001ECA40 /* RecentDocumentsView.swift */, ); path = Documents; sourceTree = ""; @@ -2873,6 +2879,7 @@ 375EC959289EEB8200751258 /* QualityProfileForm.swift in Sources */, 37D2E0D028B67DBC00F64D52 /* AnimationCompletionObserverModifier.swift in Sources */, 3727B74A27872A920021C15E /* VisualEffectBlur-iOS.swift in Sources */, + 3709528829283A21001ECA40 /* RecentDocumentsView.swift in Sources */, 37977583268922F600DD52A8 /* InvidiousAPI.swift in Sources */, 374AB3D728BCAF0000DF56FB /* SeekModel.swift in Sources */, 37130A5F277657300033018A /* PersistenceController.swift in Sources */, @@ -2916,6 +2923,7 @@ 37CC3F4C270CFE1700608308 /* PlayerQueueView.swift in Sources */, 37FFC440272734C3009FFD26 /* Throttle.swift in Sources */, 37DD9DB42785D58D00539416 /* RefreshControlModifier.swift in Sources */, + 3709528A29283E14001ECA40 /* NoDocumentsView.swift in Sources */, 3705B182267B4E4900704544 /* TrendingCategory.swift in Sources */, 378AE940274EDFB5006A4EE1 /* Tint+Backport.swift in Sources */, 376BE50927347B5F009AD608 /* SettingsHeader.swift in Sources */,