Show recent channels/playlists in search in tab navigation

This commit is contained in:
Arkadiusz Fal 2022-01-06 17:55:56 +01:00
parent f29dc792c2
commit 3495ecf693
11 changed files with 115 additions and 38 deletions

View File

@ -46,7 +46,8 @@ final class NavigationModel: ObservableObject {
player: PlayerModel, player: PlayerModel,
recents: RecentsModel, recents: RecentsModel,
navigation: NavigationModel, navigation: NavigationModel,
navigationStyle: NavigationStyle navigationStyle: NavigationStyle,
delay: Bool = false
) { ) {
let recent = RecentItem(from: channel) let recent = RecentItem(from: channel)
#if os(macOS) #if os(macOS)
@ -61,9 +62,48 @@ final class NavigationModel: ObservableObject {
} }
if navigationStyle == .tab { if navigationStyle == .tab {
if delay {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
openRecent() openRecent()
} }
} else {
openRecent()
}
} else if navigationStyle == .sidebar {
openRecent()
navigation.sidebarSectionChanged.toggle()
navigation.tabSelection = .recentlyOpened(recent.tag)
}
}
static func openChannelPlaylist(
_ playlist: ChannelPlaylist,
player: PlayerModel,
recents: RecentsModel,
navigation: NavigationModel,
navigationStyle: NavigationStyle,
delay: Bool = false
) {
let recent = RecentItem(from: playlist)
#if os(macOS)
Windows.main.open()
#else
player.hide()
#endif
let openRecent = {
recents.add(recent)
navigation.presentingPlaylist = true
}
if navigationStyle == .tab {
if delay {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
openRecent()
}
} else {
openRecent()
}
} else if navigationStyle == .sidebar { } else if navigationStyle == .sidebar {
openRecent() openRecent()
navigation.sidebarSectionChanged.toggle() navigation.sidebarSectionChanged.toggle()

View File

@ -55,6 +55,15 @@ final class RecentsModel: ObservableObject {
return nil return nil
} }
static func symbolSystemImage(_ name: String) -> String {
let firstLetter = name.first?.lowercased()
let regex = #"^[a-z0-9]$"#
let symbolName = firstLetter?.range(of: regex, options: .regularExpression) != nil ? firstLetter! : "questionmark"
return "\(symbolName).circle"
}
} }
struct RecentItem: Defaults.Serializable, Identifiable { struct RecentItem: Defaults.Serializable, Identifiable {

View File

@ -78,6 +78,7 @@ final class SearchModel: ObservableObject {
func loadSuggestions(_ query: String) { func loadSuggestions(_ query: String) {
guard !query.isEmpty else { guard !query.isEmpty else {
querySuggestions.replace([])
return return
} }

View File

@ -120,13 +120,4 @@ struct AppSidebarNavigation: View {
return .automatic return .automatic
#endif #endif
} }
static func symbolSystemImage(_ name: String) -> String {
let firstLetter = name.first?.lowercased()
let regex = #"^[a-z0-9]$"#
let symbolName = firstLetter?.range(of: regex, options: .regularExpression) != nil ? firstLetter! : "questionmark"
return "\(symbolName).circle"
}
} }

View File

@ -11,7 +11,7 @@ struct AppSidebarPlaylists: View {
NavigationLink(tag: TabSelection.playlist(playlist.id), selection: $navigation.tabSelection) { NavigationLink(tag: TabSelection.playlist(playlist.id), selection: $navigation.tabSelection) {
LazyView(PlaylistVideosView(playlist)) LazyView(PlaylistVideosView(playlist))
} label: { } label: {
Label(playlist.title, systemImage: AppSidebarNavigation.symbolSystemImage(playlist.title)) Label(playlist.title, systemImage: RecentsModel.symbolSystemImage(playlist.title))
.backport .backport
.badge(Text("\(playlist.videos.count)")) .badge(Text("\(playlist.videos.count)"))
} }

View File

@ -88,6 +88,6 @@ struct RecentNavigationLink<DestinationContent: View>: View {
} }
var labelSystemImage: String { var labelSystemImage: String {
systemImage != nil ? systemImage! : AppSidebarNavigation.symbolSystemImage(recent.title) systemImage != nil ? systemImage! : RecentsModel.symbolSystemImage(recent.title)
} }
} }

View File

@ -11,7 +11,7 @@ struct AppSidebarSubscriptions: View {
NavigationLink(tag: TabSelection.channel(channel.id), selection: $navigation.tabSelection) { NavigationLink(tag: TabSelection.channel(channel.id), selection: $navigation.tabSelection) {
LazyView(ChannelVideosView(channel: channel)) LazyView(ChannelVideosView(channel: channel))
} label: { } label: {
Label(channel.name, systemImage: AppSidebarNavigation.symbolSystemImage(channel.name)) Label(channel.name, systemImage: RecentsModel.symbolSystemImage(channel.name))
} }
.contextMenu { .contextMenu {
Button("Unsubscribe") { Button("Unsubscribe") {

View File

@ -15,6 +15,8 @@ struct AppTabNavigation: View {
@Default(.visibleSections) private var visibleSections @Default(.visibleSections) private var visibleSections
let persistenceController = PersistenceController.shared
var body: some View { var body: some View {
TabView(selection: navigation.tabSelectionBinding) { TabView(selection: navigation.tabSelectionBinding) {
if visibleSections.contains(.favorites) { if visibleSections.contains(.favorites) {
@ -42,14 +44,11 @@ struct AppTabNavigation: View {
.id(accounts.current?.id ?? "") .id(accounts.current?.id ?? "")
.environment(\.navigationStyle, .tab) .environment(\.navigationStyle, .tab)
.background( .background(
EmptyView().sheet(isPresented: $navigation.presentingChannel, onDismiss: { EmptyView().sheet(isPresented: $navigation.presentingChannel) {
if let channel = recents.presentedChannel {
recents.close(RecentItem(from: channel))
}
}) {
if let channel = recents.presentedChannel { if let channel = recents.presentedChannel {
NavigationView { NavigationView {
ChannelVideosView(channel: channel) ChannelVideosView(channel: channel)
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environment(\.inChannelView, true) .environment(\.inChannelView, true)
.environment(\.inNavigationView, true) .environment(\.inNavigationView, true)
.environmentObject(accounts) .environmentObject(accounts)
@ -64,14 +63,11 @@ struct AppTabNavigation: View {
} }
) )
.background( .background(
EmptyView().sheet(isPresented: $navigation.presentingPlaylist, onDismiss: { EmptyView().sheet(isPresented: $navigation.presentingPlaylist) {
if let playlist = recents.presentedPlaylist {
recents.close(RecentItem(from: playlist))
}
}) {
if let playlist = recents.presentedPlaylist { if let playlist = recents.presentedPlaylist {
NavigationView { NavigationView {
ChannelPlaylistView(playlist: playlist) ChannelPlaylistView(playlist: playlist)
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environment(\.inNavigationView, true) .environment(\.inNavigationView, true)
.environmentObject(accounts) .environmentObject(accounts)
.environmentObject(navigation) .environmentObject(navigation)
@ -87,6 +83,7 @@ struct AppTabNavigation: View {
.background( .background(
EmptyView().fullScreenCover(isPresented: $player.presentingPlayer) { EmptyView().fullScreenCover(isPresented: $player.presentingPlayer) {
videoPlayer videoPlayer
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environment(\.navigationStyle, .tab) .environment(\.navigationStyle, .tab)
} }
) )

View File

@ -21,6 +21,8 @@ struct SearchView: View {
@Environment(\.navigationStyle) private var navigationStyle @Environment(\.navigationStyle) private var navigationStyle
@EnvironmentObject<AccountsModel> private var accounts @EnvironmentObject<AccountsModel> private var accounts
@EnvironmentObject<NavigationModel> private var navigation
@EnvironmentObject<PlayerModel> private var player
@EnvironmentObject<RecentsModel> private var recents @EnvironmentObject<RecentsModel> private var recents
@EnvironmentObject<SearchModel> private var state @EnvironmentObject<SearchModel> private var state
private var favorites = FavoritesModel.shared private var favorites = FavoritesModel.shared
@ -255,14 +257,51 @@ struct SearchView: View {
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
ForEach(recentItems) { item in ForEach(recentItems) { item in
Button(item.title) { Button {
switch item.type {
case .query:
state.queryText = item.title state.queryText = item.title
state.changeQuery { query in query.query = item.title } state.changeQuery { query in query.query = item.title }
updateFavoriteItem() updateFavoriteItem()
recents.add(item)
case .channel:
guard let channel = item.channel else {
return
}
NavigationModel.openChannel(
channel,
player: player,
recents: recents,
navigation: navigation,
navigationStyle: navigationStyle,
delay: false
)
case .playlist:
guard let playlist = item.playlist else {
return
}
NavigationModel.openChannelPlaylist(
playlist,
player: player,
recents: recents,
navigation: navigation,
navigationStyle: navigationStyle,
delay: false
)
}
} label: {
let systemImage = item.type == .query ? "magnifyingglass" :
item.type == .channel ? RecentsModel.symbolSystemImage(item.title) :
"list.and.film"
Label(item.title, systemImage: systemImage)
.lineLimit(1)
} }
.contextMenu { .contextMenu {
deleteButton(item) removeButton(item)
deleteAllButton removeAllButton
} }
} }
} }
@ -274,21 +313,21 @@ struct SearchView: View {
#endif #endif
} }
private func deleteButton(_ item: RecentItem) -> some View { private func removeButton(_ item: RecentItem) -> some View {
Button { Button {
recents.close(item) recents.close(item)
recentsChanged.toggle() recentsChanged.toggle()
} label: { } label: {
Label("Delete", systemImage: "trash") Label("Remove", systemImage: "trash")
} }
} }
private var deleteAllButton: some View { private var removeAllButton: some View {
Button { Button {
recents.clearQueries() recents.clearQueries()
recentsChanged.toggle() recentsChanged.toggle()
} label: { } label: {
Label("Delete All", systemImage: "trash.fill") Label("Remove All", systemImage: "trash.fill")
} }
} }
@ -297,7 +336,7 @@ struct SearchView: View {
} }
private var recentItems: [RecentItem] { private var recentItems: [RecentItem] {
Defaults[.recentlyOpened].filter { $0.type == .query }.reversed() Defaults[.recentlyOpened].reversed()
} }
private var searchSortOrderPicker: some View { private var searchSortOrderPicker: some View {

View File

@ -2726,7 +2726,7 @@
INFOPLIST_FILE = iOS/Info.plist; INFOPLIST_FILE = iOS/Info.plist;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UIRequiresFullScreen = YES; INFOPLIST_KEY_UIRequiresFullScreen = NO;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
@ -2758,7 +2758,7 @@
INFOPLIST_FILE = iOS/Info.plist; INFOPLIST_FILE = iOS/Info.plist;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UIRequiresFullScreen = YES; INFOPLIST_KEY_UIRequiresFullScreen = NO;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";

View File

@ -67,7 +67,7 @@ struct InstancesSettings: View {
"Are you sure you want to remove \(selectedAccount?.description ?? "") account?" "Are you sure you want to remove \(selectedAccount?.description ?? "") account?"
), ),
message: Text("This cannot be undone"), message: Text("This cannot be undone"),
primaryButton: .destructive(Text("Delete")) { primaryButton: .destructive(Text("Remove")) {
AccountsModel.remove(selectedAccount!) AccountsModel.remove(selectedAccount!)
}, },
secondaryButton: .cancel() secondaryButton: .cancel()