diff --git a/App/AppDelegate.swift b/App/AppDelegate.swift index fb15fee1..8349f6a9 100644 --- a/App/AppDelegate.swift +++ b/App/AppDelegate.swift @@ -8,6 +8,7 @@ import UIKit import SwinjectStoryboard +import Nuke @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -22,6 +23,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { if ProcessInfo.processInfo.arguments.contains("skipAnimations") { UIView.setAnimationsEnabled(false) } + if ProcessInfo.processInfo.arguments.contains("darkMode") { + window?.overrideUserInterfaceStyle = .dark + } // setup window and entry point window = UIWindow() @@ -33,10 +37,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { window?.tintColor = AppTheme.default.appTintColor window?.makeKeyAndVisible() - if ProcessInfo.processInfo.arguments.contains("darkMode") { - window?.overrideUserInterfaceStyle = .dark - } - // setup NavigationService navigationService = SwinjectStoryboard.getService() navigationService?.mainSplitViewController = mainSplitViewController @@ -47,6 +47,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // init default settings UserDefaults.standard.registerDefaults() + + // setup Nuke + DataLoader.sharedUrlCache.diskCapacity = 1024 * 1024 * 100 // 100MB } func application( diff --git a/App/Comments/CommentsViewController.swift b/App/Comments/CommentsViewController.swift index dd1f6191..1696c63e 100644 --- a/App/Comments/CommentsViewController.swift +++ b/App/Comments/CommentsViewController.swift @@ -36,7 +36,7 @@ class CommentsViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() - setupCollectionView() + setupRefreshControl() load() } @@ -44,6 +44,16 @@ class CommentsViewController: UITableViewController { tearDownHandoff() } + private func setupRefreshControl() { + let refreshControl = UIRefreshControl() + refreshControl.addTarget( + self, + action: #selector(load), + for: .valueChanged + ) + self.refreshControl = refreshControl + } + @objc private func load(showSpinner: Bool = true) { if showSpinner { tableView.backgroundView = TableViewBackgroundView.loadingBackgroundView() @@ -250,8 +260,18 @@ extension CommentsViewController { voteAction() } + let share = UIAction( + title: "Share", + image: UIImage(systemName: "square.and.arrow.up"), + identifier: UIAction.Identifier(rawValue: "share.comment") + ) { [weak self] _ in + self?.shareComment(at: indexPath) + } + let voteMenu = upvoted ? unvote : upvote - return UIMenu(title: "", image: nil, identifier: nil, children: [voteMenu]) + let shareMenu = UIMenu(title: "", options: .displayInline, children: [share]) + + return UIMenu(title: "", image: nil, identifier: nil, children: [voteMenu, shareMenu]) } } @@ -350,7 +370,7 @@ extension CommentsViewController: SwipeTableViewCellDelegate { ) case (.right, 1): - return [collapseAction(), shareAction()] + return [collapseAction()] case (.left, 1): let comment = commentsController.visibleComments[indexPath.row] @@ -366,28 +386,16 @@ extension CommentsViewController: SwipeTableViewCellDelegate { } } - private func shareAction() -> SwipeAction { - let shareAction = SwipeAction(style: .default, title: "Share") { [weak self] _, indexPath in - guard let strongSelf = self else { - return - } - let comment = strongSelf.commentsController.visibleComments[indexPath.row] - let url = comment.hackerNewsURL - let activityViewController = UIActivityViewController( - activityItems: [url], - applicationActivities: nil - ) - let cell = strongSelf.tableView.cellForRow(at: indexPath) - activityViewController.popoverPresentationController?.sourceView = cell - strongSelf.present(activityViewController, animated: true, completion: nil) - } - shareAction.backgroundColor = .systemGreen - shareAction.textColor = .white - - let iconImage = UIImage(systemName: "square.and.arrow.up")!.withTintColor(.white) - shareAction.image = iconImage - - return shareAction + private func shareComment(at indexPath: IndexPath) { + let comment = self.commentsController.visibleComments[indexPath.row] + let url = comment.hackerNewsURL + let activityViewController = UIActivityViewController( + activityItems: [url], + applicationActivities: nil + ) + let cell = self.tableView.cellForRow(at: indexPath) + activityViewController.popoverPresentationController?.sourceView = cell + self.present(activityViewController, animated: true, completion: nil) } private func collapseAction() -> SwipeAction { @@ -483,17 +491,6 @@ extension CommentsViewController: CommentDelegate { // MARK: - Handoff extension CommentsViewController { - - private func setupCollectionView() { - let refreshControl = UIRefreshControl() - refreshControl.addTarget( - self, - action: #selector(load), - for: .valueChanged - ) - self.refreshControl = refreshControl - } - private func setupHandoff(with post: Post?, activityType: ActivityType) { guard let post = post else { return diff --git a/App/Services/OnboardingService.swift b/App/Services/OnboardingService.swift index 8111f90c..d99a9440 100644 --- a/App/Services/OnboardingService.swift +++ b/App/Services/OnboardingService.swift @@ -58,12 +58,22 @@ enum OnboardingService { } private static func items() -> [WhatsNew.Item] { - let defaultBrowserItem = WhatsNew.Item( - title: "Disable swipe gestures", - subtitle: "Added a setting to disable all swipe gestures, you can long tap for a menu instead. Thanks to @ballwood for the contribution.", - image: UIImage(systemName: "hand.draw.fill") + let pullToRefreshItem = WhatsNew.Item( + title: "Pull to refresh comments", + subtitle: "Thanks @ballwood.", + image: UIImage(systemName: "arrow.clockwise") ) - return [defaultBrowserItem] + let shareCommentItem = WhatsNew.Item( + title: "Share comments", + subtitle: "Long press on a comment to share a direct link.", + image: UIImage(systemName: "bubble.left") + ) + let bugfixesItem = WhatsNew.Item( + title: "Fixes and improvements", + subtitle: "Feedback emails work with third party email apps.\n\nThumbnail cache size limited to 100MB.", + image: UIImage(systemName: "checkmark.seal") + ) + return [pullToRefreshItem, shareCommentItem, bugfixesItem] } } diff --git a/App/Settings/SettingsViewController.swift b/App/Settings/SettingsViewController.swift index 351bf0f9..f7e7c0ee 100644 --- a/App/Settings/SettingsViewController.swift +++ b/App/Settings/SettingsViewController.swift @@ -118,14 +118,24 @@ extension SettingsViewController { } private func sendFeedbackEmail() { + let appVersion = self.appVersion() ?? "" + let emailAddress = "weiran@zhang.me.uk" + let subject = "Feedback for Hackers \(appVersion)" + if MFMailComposeViewController.canSendMail() { - let appVersion = self.appVersion() ?? "" let mail = MFMailComposeViewController() mail.mailComposeDelegate = self - mail.setToRecipients(["weiran@zhang.me.uk"]) - mail.setSubject("Feedback for Hackers \(appVersion)") + mail.setToRecipients([emailAddress]) + mail.setSubject(subject) mail.setMessageBody("", isHTML: true) present(mail, animated: true) + } else { + let mailtoString = "mailto:\(emailAddress)?subject=\(subject)" + .addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! + let mailtoURL = URL(string: mailtoString)! + if UIApplication.shared.canOpenURL(mailtoURL) { + UIApplication.shared.open(mailtoURL, options: [:]) + } } } diff --git a/Extensions/HackersActionExtension/Info.plist b/Extensions/HackersActionExtension/Info.plist index 6afdcb4b..7888ba05 100644 --- a/Extensions/HackersActionExtension/Info.plist +++ b/Extensions/HackersActionExtension/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 4.6.3 + 4.6.4 CFBundleVersion - 105 + 106 NSExtension NSExtensionAttributes diff --git a/Extensions/HackersShareExtension/Info.plist b/Extensions/HackersShareExtension/Info.plist index 22effe99..220dff30 100644 --- a/Extensions/HackersShareExtension/Info.plist +++ b/Extensions/HackersShareExtension/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 4.6.3 + 4.6.4 CFBundleVersion - 105 + 106 NSExtension NSExtensionAttributes diff --git a/Hackers.xcodeproj/project.pbxproj b/Hackers.xcodeproj/project.pbxproj index cb54ac99..257870a5 100644 --- a/Hackers.xcodeproj/project.pbxproj +++ b/Hackers.xcodeproj/project.pbxproj @@ -1026,13 +1026,15 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 106; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 2KB59GPA9B; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = Extensions/HackersActionExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + MARKETING_VERSION = 4.6.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.weiranzhang.Hackers.HackersActionExtension; @@ -1059,13 +1061,15 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 106; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 2KB59GPA9B; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = Extensions/HackersActionExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + MARKETING_VERSION = 4.6.4; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.weiranzhang.Hackers.HackersActionExtension; @@ -1161,7 +1165,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = Extensions/HackersShareExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -1194,7 +1198,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = Extensions/HackersShareExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -1316,15 +1320,15 @@ CODE_SIGN_ENTITLEMENTS = "App/Supporting Files/Hackers.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 105; + CURRENT_PROJECT_VERSION = 106; DEVELOPMENT_TEAM = 2KB59GPA9B; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "App/Supporting Files/Hackers2-Prefix.pch"; INFOPLIST_FILE = "App/Supporting Files/Hackers-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 4.6.3; + MARKETING_VERSION = 4.6.4; PRODUCT_BUNDLE_IDENTIFIER = "com.weiranzhang.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Hackers; PROVISIONING_PROFILE_SPECIFIER = "Hackers Dev Profile"; @@ -1346,15 +1350,15 @@ CODE_SIGN_ENTITLEMENTS = "App/Supporting Files/Hackers.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 105; + CURRENT_PROJECT_VERSION = 106; DEVELOPMENT_TEAM = 2KB59GPA9B; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "App/Supporting Files/Hackers2-Prefix.pch"; INFOPLIST_FILE = "App/Supporting Files/Hackers-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 4.6.3; + MARKETING_VERSION = 4.6.4; PRODUCT_BUNDLE_IDENTIFIER = "com.weiranzhang.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Hackers; PROVISIONING_PROFILE_SPECIFIER = "Hackers Prod Profile"; diff --git a/Hackers.xcodeproj/xcshareddata/xcschemes/Hackers.xcscheme b/Hackers.xcodeproj/xcshareddata/xcschemes/Hackers.xcscheme index 8d9e71d7..64d68cc9 100644 --- a/Hackers.xcodeproj/xcshareddata/xcschemes/Hackers.xcscheme +++ b/Hackers.xcodeproj/xcshareddata/xcschemes/Hackers.xcscheme @@ -1,6 +1,6 @@ 2.6.2' # SwinjectStoryboard needs updating + pod 'Swinject' pod 'SwinjectStoryboard' pod 'BulletinBoard' pod 'WhatsNewKit' diff --git a/Podfile.lock b/Podfile.lock index 798500d8..515b557b 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,15 +1,15 @@ PODS: - BulletinBoard (5.0.0) - - DeviceKit (4.4.0) + - DeviceKit (4.5.2) - HNScraper (0.2.2) - Loaf (0.7.0) - - Nuke (9.5.0) + - Nuke (10.5.2) - PromiseKit/CorePromise (6.15.3) - - SwiftLint (0.43.1) - - SwiftSoup (2.3.2) - - Swinject (2.6.2) - - SwinjectStoryboard (2.2.0): - - Swinject (~> 2.6) + - SwiftLint (0.45.1) + - SwiftSoup (2.3.3) + - Swinject (2.7.1) + - SwinjectStoryboard (2.2.2): + - Swinject (~> 2.7.1) - SwipeCellKit (2.7.1) - WhatsNewKit (1.3.7) @@ -22,7 +22,7 @@ DEPENDENCIES: - PromiseKit/CorePromise - SwiftLint - SwiftSoup - - Swinject (~> 2.6.2) + - Swinject - SwinjectStoryboard - SwipeCellKit - WhatsNewKit @@ -53,18 +53,18 @@ CHECKOUT OPTIONS: SPEC CHECKSUMS: BulletinBoard: dba77053c2a7b64953f63073f7534165af2edd21 - DeviceKit: 2c5d7485a2fa10b65c0091b2952d30843a6ba06d + DeviceKit: c622fc19f795f3e0b4d75d6d11b26604338cdab3 HNScraper: d96632c361238905f2886f83675e75f2153b4dc4 Loaf: d69937cd00649f3fa260beb8b7aef0a1feb861ef - Nuke: 6f400a4ea957e09149ec335a3c6acdcc814d89e4 + Nuke: 62d6d401e2b3ce553151a6f5e2f9332878710fbd PromiseKit: 3b2b6995e51a954c46dbc550ce3da44fbfb563c5 - SwiftLint: 99f82d07b837b942dd563c668de129a03fc3fb52 - SwiftSoup: f97bc4e988c7729d6457f9642f974c617a6e2510 - Swinject: cf7458774b1b00323080a9c67e47a1ee7f9d2af1 - SwinjectStoryboard: 32512ef16c2b0ff5b8f823b23539c4a50f6d3383 + SwiftLint: 06ac37e4d38c7068e0935bb30cda95f093bec761 + SwiftSoup: 8e4844e785a975744f2c2606f8b3844ef67eb259 + Swinject: ddf78b8486dd9b71a667b852cad919ab4484478e + SwinjectStoryboard: 240ce371396da09a4c43c72ee5c148108500e184 SwipeCellKit: 3972254a826da74609926daf59b08d6c72e619ea WhatsNewKit: c87028c4059dccd113495422801914cc53f6aab0 -PODFILE CHECKSUM: 52b90088cab90c44358c46cd2a92ab6bc5e52db7 +PODFILE CHECKSUM: 01446ada016a8c363277039b82b7b1c2962beffe -COCOAPODS: 1.10.1 +COCOAPODS: 1.11.2