From 7d07e4c0680df106dbd94b7cca4e80905b3abbc2 Mon Sep 17 00:00:00 2001 From: uttiya10 <56562649+uttiya10@users.noreply.github.com> Date: Mon, 19 Apr 2021 22:55:41 -0400 Subject: [PATCH 1/5] Add New Recommendation Sources For Featured and Latest Anime --- NineAnimator.xcodeproj/project.pbxproj | 4 + ...nManager+FeaturedAnimeRecommendation.swift | 107 ++++++++++++++++++ .../UserNotificationManager.swift | 9 +- NineAnimator/Models/Recommendation.swift | 2 +- 4 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 NineAnimator/Controllers/User Notification/UserNotificationManager+FeaturedAnimeRecommendation.swift diff --git a/NineAnimator.xcodeproj/project.pbxproj b/NineAnimator.xcodeproj/project.pbxproj index 33f4544ba..4a4833e80 100644 --- a/NineAnimator.xcodeproj/project.pbxproj +++ b/NineAnimator.xcodeproj/project.pbxproj @@ -487,6 +487,7 @@ BFA0704C24DE596D00E7C8BE /* Array+SafeSubscript.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA0704B24DE596D00E7C8BE /* Array+SafeSubscript.swift */; }; BFAA489524DE0723009BE142 /* AnimeHub.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAA489424DE0723009BE142 /* AnimeHub.swift */; }; BFAA489724DE0BA6009BE142 /* AnimeHub+Featured.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAA489624DE0BA6009BE142 /* AnimeHub+Featured.swift */; }; + BFAD190B262E5EE700D944D2 /* UserNotificationManager+FeaturedAnimeRecommendation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAD190A262E5EE700D944D2 /* UserNotificationManager+FeaturedAnimeRecommendation.swift */; }; BFBAEF5224DF228B00ADA414 /* AnimeHub+Anime.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAEF5124DF228B00ADA414 /* AnimeHub+Anime.swift */; }; BFCBEE6624E0C4E100D8C54B /* FacebookParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCBEE6524E0C4E100D8C54B /* FacebookParser.swift */; }; BFD0F7D725CA35B100621241 /* AnimeTwist+SourceDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD0F7D625CA35B100621241 /* AnimeTwist+SourceDescriptor.swift */; }; @@ -1099,6 +1100,7 @@ BFA0704B24DE596D00E7C8BE /* Array+SafeSubscript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+SafeSubscript.swift"; sourceTree = ""; }; BFAA489424DE0723009BE142 /* AnimeHub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimeHub.swift; sourceTree = ""; }; BFAA489624DE0BA6009BE142 /* AnimeHub+Featured.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AnimeHub+Featured.swift"; sourceTree = ""; }; + BFAD190A262E5EE700D944D2 /* UserNotificationManager+FeaturedAnimeRecommendation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserNotificationManager+FeaturedAnimeRecommendation.swift"; sourceTree = ""; }; BFBAEF5124DF228B00ADA414 /* AnimeHub+Anime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AnimeHub+Anime.swift"; sourceTree = ""; }; BFCB22AA24DF378800406034 /* AnimeHub+Episode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AnimeHub+Episode.swift"; sourceTree = ""; }; BFCBEE6524E0C4E100D8C54B /* FacebookParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FacebookParser.swift; sourceTree = ""; }; @@ -2280,6 +2282,7 @@ children = ( 2C3545A421DA84F500321B18 /* UserNotificationManager.swift */, 2C60EAB92266A22400280637 /* UserNotificationManager+RecommendationSource.swift */, + BFAD190A262E5EE700D944D2 /* UserNotificationManager+FeaturedAnimeRecommendation.swift */, ); path = "User Notification"; sourceTree = ""; @@ -2810,6 +2813,7 @@ 2C055B0124AAB0E300FCAC25 /* MasterAnimeLinkRetriver.swift in Sources */, 2C1C716F221853AD00760A69 /* Anilist.swift in Sources */, 2C6CF24E221C2E090009D554 /* InformationSceneHeadingTableViewCell.swift in Sources */, + BFAD190B262E5EE700D944D2 /* UserNotificationManager+FeaturedAnimeRecommendation.swift in Sources */, 2C0CFC3D2263D9AB00EC793E /* DiscoveryLoadingTableViewCell.swift in Sources */, 2C2C54FF221606F800BAA76E /* User+UserDefaultKeys.swift in Sources */, 2CE5B8E0225ECAC200A2CF89 /* SetupFinishingViewController.swift in Sources */, diff --git a/NineAnimator/Controllers/User Notification/UserNotificationManager+FeaturedAnimeRecommendation.swift b/NineAnimator/Controllers/User Notification/UserNotificationManager+FeaturedAnimeRecommendation.swift new file mode 100644 index 000000000..501163954 --- /dev/null +++ b/NineAnimator/Controllers/User Notification/UserNotificationManager+FeaturedAnimeRecommendation.swift @@ -0,0 +1,107 @@ +// +// This file is part of the NineAnimator project. +// +// Copyright © 2018-2020 Marcus Zhou. All rights reserved. +// +// NineAnimator is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NineAnimator is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with NineAnimator. If not, see . +// + +extension UserNotificationManager { + class FeaturedAnimeRecommendation: RecommendationSource { + var name = "Featured Anime" + + var priority: Priority = .defaultMedium + + var shouldPresentRecommendation: Bool { + // Hide recommendation source if the currently + // selected anime source returned no featured anime + return lastDisplayedSourceHasFeatured || + (NineAnimator.default.user.source.name != currentlyLoadedSource.name) + } + + var currentlyLoadedSource = NineAnimator.default.user.source + var lastDisplayedSourceHasFeatured = true + + // This source also controls its child recommendation source + lazy var latestAnimeRecommendationSource = { LatestAnimeRecommendation(self) }() + + func shouldReload(recommendation: Recommendation) -> Bool { + // Reload if the user changed currently selected source + NineAnimator.default.user.source.name != currentlyLoadedSource.name + } + + func generateRecommendations() -> NineAnimatorPromise { + NineAnimatorPromise { + self.currentlyLoadedSource = NineAnimator.default.user.source + return self.currentlyLoadedSource.featured($0) + } .then { result in + let featuredItems = result.featured.map { + RecommendingItem(.anime($0)) + } + + self.lastDisplayedSourceHasFeatured = !featuredItems.isEmpty + + // Update child source as well + let latestAnimeItems = result.latest.map { + RecommendingItem(.anime($0)) + } + self.latestAnimeRecommendationSource.latestAnime = latestAnimeItems + + return Recommendation( + self, + items: featuredItems, + title: "Featured Anime", + subtitle: self.currentlyLoadedSource.name + ) + } + } + + /// Child source for Featured Anime Recommendation + class LatestAnimeRecommendation: RecommendationSource { + var name = "Latest Anime" + + var priority: Priority = .defaultMedium + + var shouldPresentRecommendation = false + + var latestAnime: [RecommendingItem] = [] { + didSet { + shouldPresentRecommendation = !latestAnime.isEmpty + fireDidUpdateNotification() + } + } + + let parent: FeaturedAnimeRecommendation + + init(_ parent: FeaturedAnimeRecommendation) { + self.parent = parent + } + + func shouldReload(recommendation: Recommendation) -> Bool { + recommendation.items != latestAnime + } + + func generateRecommendations() -> NineAnimatorPromise { + .success( + .init( + self, + items: latestAnime, + title: "Latest Anime", + subtitle: parent.currentlyLoadedSource.name + ) + ) + } + } + } +} diff --git a/NineAnimator/Controllers/User Notification/UserNotificationManager.swift b/NineAnimator/Controllers/User Notification/UserNotificationManager.swift index f929b7c76..0e04c39b9 100644 --- a/NineAnimator/Controllers/User Notification/UserNotificationManager.swift +++ b/NineAnimator/Controllers/User Notification/UserNotificationManager.swift @@ -53,8 +53,6 @@ class UserNotificationManager: NSObject, UNUserNotificationCenterDelegate { private let animeCachingDirectory: URL - private let subscriptionRecommendationSource = SubscribedAnimeRecommendationSource() - override init() { let fileManager = FileManager.default self.animeCachingDirectory = try! fileManager.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) @@ -73,7 +71,12 @@ class UserNotificationManager: NSObject, UNUserNotificationCenterDelegate { registerNotificationCategories() // Add anime subscription as a recommendation source - NineAnimator.default.register(additionalRecommendationSource: subscriptionRecommendationSource) + NineAnimator.default.register(additionalRecommendationSource: SubscribedAnimeRecommendationSource()) + + // Add featured and latest anime recommendation sources + let featuredRecommendationSource = FeaturedAnimeRecommendation() + NineAnimator.default.register(additionalRecommendationSource: featuredRecommendationSource) + NineAnimator.default.register(additionalRecommendationSource: featuredRecommendationSource.latestAnimeRecommendationSource) } } diff --git a/NineAnimator/Models/Recommendation.swift b/NineAnimator/Models/Recommendation.swift index 0f8830be8..39050204c 100644 --- a/NineAnimator/Models/Recommendation.swift +++ b/NineAnimator/Models/Recommendation.swift @@ -42,7 +42,7 @@ protocol RecommendationSource: AnyObject { } /// Representing a recommended item -struct RecommendingItem { +struct RecommendingItem: Hashable { enum CaptionStyle { case standard case highlight From f37f51d140836e9fe31ce570748586f99dd9ec42 Mon Sep 17 00:00:00 2001 From: uttiya10 <56562649+uttiya10@users.noreply.github.com> Date: Mon, 19 Apr 2021 23:18:06 -0400 Subject: [PATCH 2/5] Add keyboard shortcut to reload all recommendations in discovery scene --- .../DiscoverySceneViewController.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/NineAnimator/Controllers/Anime Discovery Scenes/DiscoverySceneViewController.swift b/NineAnimator/Controllers/Anime Discovery Scenes/DiscoverySceneViewController.swift index 725c1f0b4..cfb49ed85 100644 --- a/NineAnimator/Controllers/Anime Discovery Scenes/DiscoverySceneViewController.swift +++ b/NineAnimator/Controllers/Anime Discovery Scenes/DiscoverySceneViewController.swift @@ -97,6 +97,16 @@ class DiscoverySceneViewController: UITableViewController { } } + override var keyCommands: [UIKeyCommand]? { + [ + UIKeyCommand( + title: "Refresh All Recommendations", + action: #selector(reloadRecommendationList), + input: "r", modifierFlags: .command + ) + ] + } + override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { cell.makeThemable() } @@ -318,7 +328,7 @@ fileprivate extension DiscoverySceneViewController { } /// Reload the entire recommendation list - func reloadRecommendationList(shouldInformTableView: Bool = true) { + @objc func reloadRecommendationList(shouldInformTableView: Bool = true) { // Abort all previous tasks recommendationLoadingTasks = [:] recommendationList = NineAnimator From 8c796c4e66dd9e28858c2ec30b1b615a25e261a4 Mon Sep 17 00:00:00 2001 From: uttiya10 <56562649+uttiya10@users.noreply.github.com> Date: Thu, 22 Apr 2021 18:26:43 -0400 Subject: [PATCH 3/5] Mark keyboard shortcuts in DiscoveryScene as only for iOS 13+ --- .../Anime Discovery Scenes/DiscoverySceneViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NineAnimator/Controllers/Anime Discovery Scenes/DiscoverySceneViewController.swift b/NineAnimator/Controllers/Anime Discovery Scenes/DiscoverySceneViewController.swift index cfb49ed85..e8b7525b3 100644 --- a/NineAnimator/Controllers/Anime Discovery Scenes/DiscoverySceneViewController.swift +++ b/NineAnimator/Controllers/Anime Discovery Scenes/DiscoverySceneViewController.swift @@ -97,12 +97,14 @@ class DiscoverySceneViewController: UITableViewController { } } + @available(iOS 13.0, *) override var keyCommands: [UIKeyCommand]? { [ UIKeyCommand( title: "Refresh All Recommendations", action: #selector(reloadRecommendationList), - input: "r", modifierFlags: .command + input: "r", + modifierFlags: .command ) ] } From 91bcc0f45974e4757c62b77c14f3ecaa6f3b08eb Mon Sep 17 00:00:00 2001 From: uttiya10 <56562649+uttiya10@users.noreply.github.com> Date: Thu, 22 Apr 2021 18:52:50 -0400 Subject: [PATCH 4/5] Update recommendation subtitles --- .../UserNotificationManager+FeaturedAnimeRecommendation.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NineAnimator/Controllers/User Notification/UserNotificationManager+FeaturedAnimeRecommendation.swift b/NineAnimator/Controllers/User Notification/UserNotificationManager+FeaturedAnimeRecommendation.swift index 501163954..3ca5e053e 100644 --- a/NineAnimator/Controllers/User Notification/UserNotificationManager+FeaturedAnimeRecommendation.swift +++ b/NineAnimator/Controllers/User Notification/UserNotificationManager+FeaturedAnimeRecommendation.swift @@ -62,7 +62,7 @@ extension UserNotificationManager { self, items: featuredItems, title: "Featured Anime", - subtitle: self.currentlyLoadedSource.name + subtitle: "On \(self.currentlyLoadedSource.name)" ) } } @@ -98,7 +98,7 @@ extension UserNotificationManager { self, items: latestAnime, title: "Latest Anime", - subtitle: parent.currentlyLoadedSource.name + subtitle: "On \(parent.currentlyLoadedSource.name)" ) ) } From 2c1b220552ece456517093fe58b7b800240f78dc Mon Sep 17 00:00:00 2001 From: uttiya10 <56562649+uttiya10@users.noreply.github.com> Date: Thu, 22 Apr 2021 19:13:58 -0400 Subject: [PATCH 5/5] Fix availability check when building for iOS --- .../DiscoverySceneViewController.swift | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/NineAnimator/Controllers/Anime Discovery Scenes/DiscoverySceneViewController.swift b/NineAnimator/Controllers/Anime Discovery Scenes/DiscoverySceneViewController.swift index e8b7525b3..ee466a761 100644 --- a/NineAnimator/Controllers/Anime Discovery Scenes/DiscoverySceneViewController.swift +++ b/NineAnimator/Controllers/Anime Discovery Scenes/DiscoverySceneViewController.swift @@ -97,16 +97,17 @@ class DiscoverySceneViewController: UITableViewController { } } - @available(iOS 13.0, *) - override var keyCommands: [UIKeyCommand]? { - [ - UIKeyCommand( - title: "Refresh All Recommendations", - action: #selector(reloadRecommendationList), - input: "r", - modifierFlags: .command - ) - ] + override open var keyCommands: [UIKeyCommand]? { + if #available(iOS 13.0, *) { + return [ + UIKeyCommand( + title: "Refresh All Recommendations", + action: #selector(reloadRecommendationList), + input: "r", + modifierFlags: .command + ) + ] + } else { return nil } } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {