From 77841b41c081f91be9d2cd7151717baca3483437 Mon Sep 17 00:00:00 2001 From: Weiran Zhang Date: Mon, 4 Apr 2022 09:45:18 +0100 Subject: [PATCH 1/8] Updated SwiftLint --- Podfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Podfile.lock b/Podfile.lock index 7b4086e8..c948afb8 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -5,7 +5,7 @@ PODS: - Loaf (0.7.0) - Nuke (10.7.1) - PromiseKit/CorePromise (6.17.0) - - SwiftLint (0.46.5) + - SwiftLint (0.47.0) - SwiftSoup (2.3.8) - Swinject (2.7.1) - SwinjectStoryboard (2.2.2): @@ -58,7 +58,7 @@ SPEC CHECKSUMS: Loaf: d69937cd00649f3fa260beb8b7aef0a1feb861ef Nuke: 279f17a599fd1c83cf51de5e0e1f2db143a287b0 PromiseKit: 996202d4cae72b7801b312abef4cd2f995b2978a - SwiftLint: 2c16e8fa99dac2e440dc0c70ea74d2532048f6cf + SwiftLint: d41cc46a2ae58ac6d9f26954bc89f1d72e71fdef SwiftSoup: ba8ea2c91151881a298cba4b0628ef31e835a77f Swinject: ddf78b8486dd9b71a667b852cad919ab4484478e SwinjectStoryboard: 240ce371396da09a4c43c72ee5c148108500e184 From 2942e668d7328ea16de5ff3222c10aa78473d16c Mon Sep 17 00:00:00 2001 From: Weiran Zhang Date: Mon, 4 Apr 2022 12:04:36 +0100 Subject: [PATCH 2/8] Quick and dirty new Login UI --- .swiftlint.yml | 4 +- App/Settings/LoginView.swift | 161 ++++++++++++++++++++++ App/Settings/SettingsViewController.swift | 7 +- Hackers.xcodeproj/project.pbxproj | 18 ++- 4 files changed, 181 insertions(+), 9 deletions(-) create mode 100644 App/Settings/LoginView.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index c693ee8f..9e2ff3f7 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -19,4 +19,6 @@ type_name: identifier_name: excluded: - id - - by \ No newline at end of file + - by + allowed_symbols: + - _body \ No newline at end of file diff --git a/App/Settings/LoginView.swift b/App/Settings/LoginView.swift new file mode 100644 index 00000000..1b92b772 --- /dev/null +++ b/App/Settings/LoginView.swift @@ -0,0 +1,161 @@ +// +// LoginView.swift +// Hackers +// +// Created by Weiran Zhang on 04/04/2022. +// Copyright © 2022 Glass Umbrella. All rights reserved. +// + +import SwiftUI +import Swinject +import SwinjectStoryboard +import PromiseKit + +struct LoginView: View { + @State private var username: String = "" + @State private var password: String = "" + @State private var isAuthenticating = false + @State private var showAlert = false + + @Inject private var sessionService: SessionService + + @Environment(\.presentationMode) var presentationMode + + var body: some View { + VStack { + Text("Login to Hacker News") + .font(.largeTitle) + .padding(.bottom, 30) + + TextField("Username", text: $username) + .autocapitalization(.none) + .disableAutocorrection(true) + .textFieldStyle(RoundedTextField()) + .textContentType(.username) + + SecureField("Password", text: $password) + .textFieldStyle(RoundedTextField()) + .textContentType(.password) + + Text("Hackers never stores your password") + .foregroundColor(Color.secondary) + .font(.footnote) + + Button("Login") { + isAuthenticating = true + sessionService.authenticate(username: username, password: password) + .done { _ in + presentationMode.wrappedValue.dismiss() + }.ensure { + NotificationCenter.default.post(name: Notification.Name.refreshRequired, object: nil) + isAuthenticating = false + }.catch { _ in + showAlert = true + password = "" + } + }.buttonStyle(FilledButton()) + .padding(.top, 30) + .disabled(isAuthenticating) + .alert(isPresented: $showAlert) { + Alert( + title: Text("Login Failed"), + message: Text("Error logging into Hacker News, check your username and password.") + ) + } + + LabelledDivider(label: "or") + + Link(destination: URL(string: "https://news.ycombinator.com/login")!) { + HStack { + Text("Register on Hacker News") + Image(systemName: "rectangle.portrait.and.arrow.right") + } + .padding() + } + } + } +} + +struct RoundedTextField: TextFieldStyle { + func _body(configuration: TextField) -> some View { + configuration + .padding(.all, 10) + .overlay( + RoundedRectangle(cornerRadius: 5) + .stroke(Color.secondary, lineWidth: 0.5) + ) + .padding(.horizontal, 20) + } +} + +struct FilledButton: ButtonStyle { + func makeBody(configuration: Configuration) -> some View { + configuration + .label + .padding() + .padding(.horizontal, 50) + .frame(maxWidth: .infinity) + .foregroundColor(Color.white) + .background(Color.accentColor) + .cornerRadius(5) + .padding(.horizontal, 20) + } +} + +struct LabelledDivider: View { + + let label: String + let horizontalPadding: CGFloat + let color: Color + + init(label: String, horizontalPadding: CGFloat = 20, color: Color = .gray) { + self.label = label + self.horizontalPadding = horizontalPadding + self.color = color + } + + var body: some View { + HStack { + line + Text(label).foregroundColor(color) + line + } + } + + var line: some View { + VStack { Divider().background(color) }.padding(horizontalPadding) + } +} + +@propertyWrapper +struct Inject { + let wrappedValue: Component + init() { + self.wrappedValue = Resolver.shared.resolve(Component.self) + } +} + +class Resolver { + static let shared = Resolver() + private let container = buildContainer() + + func resolve(_ type: T.Type) -> T { + container.resolve(T.self)! + } +} + +struct LoginView_Previews: PreviewProvider { + static var previews: some View { + LoginView() + } +} + +func buildContainer() -> Container { + let container = SwinjectStoryboard.defaultContainer + + container.register(SessionService.self) { _ in + return SessionService() + }.inObjectScope(.container) + + return container +} diff --git a/App/Settings/SettingsViewController.swift b/App/Settings/SettingsViewController.swift index f7e7c0ee..d92fa71c 100644 --- a/App/Settings/SettingsViewController.swift +++ b/App/Settings/SettingsViewController.swift @@ -7,6 +7,7 @@ // import UIKit +import SwiftUI import SafariServices import MessageUI import PromiseKit @@ -144,7 +145,11 @@ extension SettingsViewController { } private func login() { - authenticationUIService?.showAuthentication() + let loginView = LoginView() + let loginHC = UIHostingController(rootView: loginView) + present(loginHC, animated: true) + + //authenticationUIService?.showAuthentication() } } diff --git a/Hackers.xcodeproj/project.pbxproj b/Hackers.xcodeproj/project.pbxproj index 7dd6a59a..3b99dfcd 100644 --- a/Hackers.xcodeproj/project.pbxproj +++ b/Hackers.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ 2481BD61247C11AE0088CA2B /* HackersKit+CommentVoting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2481BD60247C11AE0088CA2B /* HackersKit+CommentVoting.swift */; }; 2481BD63247C12540088CA2B /* HNScraperShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2481BD62247C12540088CA2B /* HNScraperShim.swift */; }; 2481BD65247C1B380088CA2B /* HackersKit+PostVoting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2481BD64247C1B380088CA2B /* HackersKit+PostVoting.swift */; }; + 2486D7D527FAEBFC007504C6 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2486D7D427FAEBFC007504C6 /* LoginView.swift */; }; 24993C55229C2BD400AF1891 /* AuthenticationUIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24993C54229C2BD400AF1891 /* AuthenticationUIService.swift */; }; 24993C57229C768700AF1891 /* AuthenticationBulletinPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24993C56229C768700AF1891 /* AuthenticationBulletinPage.swift */; }; 249A7E32194374550059C079 /* CommentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249A7E31194374550059C079 /* CommentTableViewCell.swift */; }; @@ -160,6 +161,7 @@ 2481BD60247C11AE0088CA2B /* HackersKit+CommentVoting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HackersKit+CommentVoting.swift"; sourceTree = ""; }; 2481BD62247C12540088CA2B /* HNScraperShim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HNScraperShim.swift; sourceTree = ""; }; 2481BD64247C1B380088CA2B /* HackersKit+PostVoting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HackersKit+PostVoting.swift"; sourceTree = ""; }; + 2486D7D427FAEBFC007504C6 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; 24993C54229C2BD400AF1891 /* AuthenticationUIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationUIService.swift; sourceTree = ""; }; 24993C56229C768700AF1891 /* AuthenticationBulletinPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationBulletinPage.swift; sourceTree = ""; }; 249A7E31194374550059C079 /* CommentTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentTableViewCell.swift; sourceTree = ""; }; @@ -359,6 +361,7 @@ 2464D21A209E1FA100C7F8FC /* SettingsViewController.swift */, 2464D21C209E208800C7F8FC /* SettingsTableViewCell.swift */, 2464D21E209E27FE00C7F8FC /* UserDefaultsExtensions.swift */, + 2486D7D427FAEBFC007504C6 /* LoginView.swift */, ); path = Settings; sourceTree = ""; @@ -745,7 +748,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "buildNumber=$(xcodebuild -showBuildSettings -project Hackers.xcodeproj | pcregrep -o1 \"PROJECT_VERSION = ([0-9a-f\\-]+)\")\nmarketingVersion=$(xcodebuild -showBuildSettings -project Hackers.xcodeproj | pcregrep -o1 \"MARKETING_VERSION = ([0-9a-f\\-.]+)\")\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$SRCROOT/Extensions/HackersShareExtension/Info.plist\"\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString $marketingVersion\" \"$SRCROOT/Extensions/HackersShareExtension/Info.plist\"\n"; + shellScript = "if [ $ENABLE_PREVIEWS == \"NO\" ]\nthen\n buildNumber=$(xcodebuild -showBuildSettings -project Hackers.xcodeproj | pcregrep -o1 \"PROJECT_VERSION = ([0-9a-f\\-]+)\")\n marketingVersion=$(xcodebuild -showBuildSettings -project Hackers.xcodeproj | pcregrep -o1 \"MARKETING_VERSION = ([0-9a-f\\-.]+)\")\n /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$SRCROOT/Extensions/HackersShareExtension/Info.plist\"\n /usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString $marketingVersion\" \"$SRCROOT/Extensions/HackersShareExtension/Info.plist\"\nelse\n echo \"Skipping the script because of preview mode\"\nfi\n"; }; 2419D03E248AC2CA00740184 /* Update Version Numbers */ = { isa = PBXShellScriptBuildPhase; @@ -763,11 +766,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "buildNumber=$(xcodebuild -showBuildSettings -project Hackers.xcodeproj | pcregrep -o1 \"PROJECT_VERSION = ([0-9a-f\\-]+)\")\nmarketingVersion=$(xcodebuild -showBuildSettings -project Hackers.xcodeproj | pcregrep -o1 \"MARKETING_VERSION = ([0-9a-f\\-.]+)\")\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$SRCROOT/Extensions/HackersActionExtension/Info.plist\"\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString $marketingVersion\" \"$SRCROOT/Extensions/HackersActionExtension/Info.plist\"\n"; + shellScript = "if [ $ENABLE_PREVIEWS == \"NO\" ]\nthen\n buildNumber=$(xcodebuild -showBuildSettings -project Hackers.xcodeproj | pcregrep -o1 \"PROJECT_VERSION = ([0-9a-f\\-]+)\")\n marketingVersion=$(xcodebuild -showBuildSettings -project Hackers.xcodeproj | pcregrep -o1 \"MARKETING_VERSION = ([0-9a-f\\-.]+)\")\n /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$SRCROOT/Extensions/HackersActionExtension/Info.plist\"\n /usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString $marketingVersion\" \"$SRCROOT/Extensions/HackersActionExtension/Info.plist\"\nelse\n echo \"Skipping the script because of preview mode\"\nfi\n"; }; 244DE6BB1E599EB6005B441E /* TODO as Warnings */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; + buildActionMask = 8; files = ( ); inputPaths = ( @@ -775,13 +778,13 @@ name = "TODO as Warnings"; outputPaths = ( ); - runOnlyForDeploymentPostprocessing = 0; + runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "KEYWORDS=\"TODO:|FIXME:|\\?\\?\\?:|\\!\\!\\!:\"\nfind \".\" \\( -name \"*.h\" -or -name \"*.m\" -or -name \"*.swift\" \\) -not -path \"./Pods/*\" -print0 | \n xargs -0 egrep --with-filename --line-number --only-matching \"($KEYWORDS).*\\$\" | \n perl -p -e \"s/($KEYWORDS)/ warning: \\$1/\"\n"; + shellScript = "KEYWORDS=\"TODO:|FIXME:|\\?\\?\\?:|\\!\\!\\!:\"\nfind \".\" \\( -name \"*.h\" -or -name \"*.m\" -or -name \"*.swift\" \\) -not -path \"./Pods/*\" -print0 | \nxargs -0 egrep --with-filename --line-number --only-matching \"($KEYWORDS).*\\$\" | \nperl -p -e \"s/($KEYWORDS)/ warning: \\$1/\"\n"; }; 249B8F8022A29B2E002A9EC5 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; + buildActionMask = 12; files = ( ); inputFileListPaths = ( @@ -794,7 +797,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\"\n"; + shellScript = "if [ $ENABLE_PREVIEWS == \"NO\" ]\nthen\n \"${PODS_ROOT}/SwiftLint/swiftlint\"\nelse\n echo \"Skipping the script because of preview mode\"\nfi\n"; }; 32F4D363AD3C2A95650B70E3 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -934,6 +937,7 @@ 2454B9041F655426005A98C7 /* EmptyViewController.swift in Sources */, 2464D21D209E208800C7F8FC /* SettingsTableViewCell.swift in Sources */, 24D1846124B396BD0017E4F4 /* UIViewController+Deselection.swift in Sources */, + 2486D7D527FAEBFC007504C6 /* LoginView.swift in Sources */, 24D811A51FF90A58000951C3 /* TouchableTextView.swift in Sources */, 24B982DD22BE65E2000D8913 /* SettingsView.swift in Sources */, 24CEAA9B1F8916970050DEDF /* UIViewExtensions.swift in Sources */, From 3870cda290a5fb3fd99f29cbef6d6570994b4082 Mon Sep 17 00:00:00 2001 From: Weiran Zhang Date: Mon, 4 Apr 2022 12:31:01 +0100 Subject: [PATCH 3/8] Removed old Login UI --- App/Comments/CommentsViewController.swift | 5 +- .../SwinjectStoryboardExtensions.swift | 13 +- App/Extensions/SwinjectSwiftUI.swift | 38 +++++ App/Feed/FeedCollectionViewController.swift | 8 +- App/Services/AuthenticationHelper.swift | 31 +++++ .../AuthenticationBulletinPage.swift | 114 --------------- .../AuthenticationUIService.swift | 116 ---------------- App/Settings/LoginView.swift | 131 ++++++++---------- App/Settings/SettingsViewController.swift | 7 +- App/Shared/SwipeCellKitActions.swift | 16 +-- Hackers.xcodeproj/project.pbxproj | 24 ++-- 11 files changed, 148 insertions(+), 355 deletions(-) create mode 100644 App/Extensions/SwinjectSwiftUI.swift create mode 100644 App/Services/AuthenticationHelper.swift delete mode 100644 App/Services/AuthenticationUIService/AuthenticationBulletinPage.swift delete mode 100644 App/Services/AuthenticationUIService/AuthenticationUIService.swift diff --git a/App/Comments/CommentsViewController.swift b/App/Comments/CommentsViewController.swift index 3366bd40..2b2e0150 100644 --- a/App/Comments/CommentsViewController.swift +++ b/App/Comments/CommentsViewController.swift @@ -14,7 +14,6 @@ import PromiseKit import Loaf class CommentsViewController: UITableViewController { - var authenticationUIService: AuthenticationUIService? var swipeCellKitActions: SwipeCellKitActions? var navigationService: NavigationService? @@ -288,7 +287,7 @@ extension CommentsViewController { switch error { case .unauthenticated: self.present( - self.authenticationUIService!.unauthenticatedAlertController(), + AuthenticationHelper.unauthenticatedAlertController(self), animated: true ) default: @@ -324,7 +323,7 @@ extension CommentsViewController { switch error { case .unauthenticated: self.present( - self.authenticationUIService!.unauthenticatedAlertController(), + AuthenticationHelper.unauthenticatedAlertController(self), animated: true ) default: diff --git a/App/Extensions/SwinjectStoryboardExtensions.swift b/App/Extensions/SwinjectStoryboardExtensions.swift index 93ff2149..35a48400 100644 --- a/App/Extensions/SwinjectStoryboardExtensions.swift +++ b/App/Extensions/SwinjectStoryboardExtensions.swift @@ -12,28 +12,19 @@ extension SwinjectStoryboard { @objc class func setup() { let container = defaultContainer - container.storyboardInitCompleted(FeedCollectionViewController.self) { resolver, controller in - controller.authenticationUIService = resolver.resolve(AuthenticationUIService.self)! - } container.storyboardInitCompleted(CommentsViewController.self) { resolver, controller in - controller.authenticationUIService = resolver.resolve(AuthenticationUIService.self)! controller.swipeCellKitActions = resolver.resolve(SwipeCellKitActions.self)! controller.navigationService = resolver.resolve(NavigationService.self)! } container.storyboardInitCompleted(SettingsViewController.self) { resolver, controller in controller.sessionService = resolver.resolve(SessionService.self)! - controller.authenticationUIService = resolver.resolve(AuthenticationUIService.self)! } container.register(SessionService.self) { _ in SessionService() }.inObjectScope(.container) - container.register(AuthenticationUIService.self) { resolver in - AuthenticationUIService(sessionService: resolver.resolve(SessionService.self)!) - }.inObjectScope(.container) - container.register(SwipeCellKitActions.self) { resolver in - SwipeCellKitActions( - authenticationUIService: resolver.resolve(AuthenticationUIService.self)!) + container.register(SwipeCellKitActions.self) { _ in + SwipeCellKitActions() } container.register(NavigationService.self) { _ in NavigationService() diff --git a/App/Extensions/SwinjectSwiftUI.swift b/App/Extensions/SwinjectSwiftUI.swift new file mode 100644 index 00000000..b885848a --- /dev/null +++ b/App/Extensions/SwinjectSwiftUI.swift @@ -0,0 +1,38 @@ +// +// SwinjectSwiftUI.swift +// Hackers +// +// Created by Weiran Zhang on 04/04/2022. +// Copyright © 2022 Glass Umbrella. All rights reserved. +// + +import SwiftUI +import Swinject +import SwinjectStoryboard + +@propertyWrapper +struct Inject { + let wrappedValue: Component + init() { + self.wrappedValue = Resolver.shared.resolve(Component.self) + } +} + +class Resolver { + static let shared = Resolver() + private let container = buildContainer() + + func resolve(_ type: T.Type) -> T { + container.resolve(T.self)! + } +} + +func buildContainer() -> Container { + let container = SwinjectStoryboard.defaultContainer + + container.register(SessionService.self) { _ in + return SessionService() + }.inObjectScope(.container) + + return container +} diff --git a/App/Feed/FeedCollectionViewController.swift b/App/Feed/FeedCollectionViewController.swift index 35df18bc..317f2d2b 100644 --- a/App/Feed/FeedCollectionViewController.swift +++ b/App/Feed/FeedCollectionViewController.swift @@ -14,8 +14,6 @@ import Loaf class FeedCollectionViewController: UIViewController { @IBOutlet weak var collectionView: UICollectionView! - var authenticationUIService: AuthenticationUIService? - private lazy var dataSource = makeDataSource() private lazy var viewModel = FeedViewModel() @@ -299,16 +297,14 @@ extension FeedCollectionViewController { } private func handleVoteError(error: Error) { - guard - let error = error as? HackersKitError, - let authenticationUIService = self.authenticationUIService else { + guard let error = error as? HackersKitError else { return } switch error { case .unauthenticated: self.present( - authenticationUIService.unauthenticatedAlertController(), + AuthenticationHelper.unauthenticatedAlertController(self), animated: true ) default: diff --git a/App/Services/AuthenticationHelper.swift b/App/Services/AuthenticationHelper.swift new file mode 100644 index 00000000..4049aaf4 --- /dev/null +++ b/App/Services/AuthenticationHelper.swift @@ -0,0 +1,31 @@ +// +// AuthenticationHelper.swift +// Hackers +// +// Created by Weiran Zhang on 04/04/2022. +// Copyright © 2022 Weiran Zhang. All rights reserved. +// + +import UIKit +import SwiftUI + +enum AuthenticationHelper { + static func showLoginView(_ presentingViewController: UIViewController) { + let loginView = UIHostingController(rootView: LoginView()) + presentingViewController.present(loginView, animated: true) + } + + static func unauthenticatedAlertController(_ presentingViewController: UIViewController) -> UIAlertController { + let unauthenticatedMessage = "You're not logged into Hacker News. Do you want to login now?" + let authenticationAlert = UIAlertController( + title: "Not logged in", + message: unauthenticatedMessage, + preferredStyle: .alert + ) + authenticationAlert.addAction(UIAlertAction(title: "Not Now", style: .cancel, handler: nil)) + authenticationAlert.addAction(UIAlertAction(title: "Login", style: .default, handler: { _ in + self.showLoginView(presentingViewController) + })) + return authenticationAlert + } +} diff --git a/App/Services/AuthenticationUIService/AuthenticationBulletinPage.swift b/App/Services/AuthenticationUIService/AuthenticationBulletinPage.swift deleted file mode 100644 index 243c3f61..00000000 --- a/App/Services/AuthenticationUIService/AuthenticationBulletinPage.swift +++ /dev/null @@ -1,114 +0,0 @@ -// -// AuthenticationBulletinPage.swift -// Hackers -// -// Created by Weiran Zhang on 27/05/2019. -// Copyright © 2019 Weiran Zhang. All rights reserved. -// - -import BLTNBoard - -class AuthenticationBulletinPage: BLTNPageItem { - @objc var usernameTextField: UITextField! - @objc var passwordTextField: UITextField! - - override func willDisplay() { - usernameTextField.becomeFirstResponder() - } - - override func makeViewsUnderDescription(with interfaceBuilder: BLTNInterfaceBuilder) -> [UIView]? { - let usernameTextField = textField(with: AppTheme.default) - usernameTextField.delegate = self - usernameTextField.textContentType = .username - usernameTextField.autocorrectionType = .no - usernameTextField.autocapitalizationType = .none - usernameTextField.returnKeyType = .next - usernameTextField.attributedPlaceholder = themedAttributedString( - for: "Username", - color: AppTheme.default.lightTextColor - ) - - let passwordTextField = textField(with: AppTheme.default) - passwordTextField.delegate = self - passwordTextField.isSecureTextEntry = true - passwordTextField.textContentType = .password - passwordTextField.returnKeyType = .done - passwordTextField.attributedPlaceholder = themedAttributedString( - for: "Password", - color: AppTheme.default.lightTextColor - ) - - self.usernameTextField = usernameTextField - self.passwordTextField = passwordTextField - - return [usernameTextField, passwordTextField] - } - - private func themedAttributedString(for string: String, color: UIColor) -> NSAttributedString { - let attributes = [NSAttributedString.Key.foregroundColor: color] - return NSAttributedString(string: string, attributes: attributes) - } - - private func textField(with theme: AppTheme) -> UITextField { - let textField = UITextField() - textField.borderStyle = .roundedRect - textField.backgroundColor = theme.backgroundColor - textField.textColor = theme.textColor - textField.layer.borderColor = theme.separatorColor.cgColor - textField.layer.borderWidth = 1.0 - textField.layer.cornerRadius = 8 - textField.layer.masksToBounds = true - return textField - } - - override func actionButtonTapped(sender: UIButton) { - usernameTextField.resignFirstResponder() - passwordTextField.resignFirstResponder() - super.actionButtonTapped(sender: sender) - } - - func set(state: CredentialsState) { - switch state { - case .standard: - usernameTextField.backgroundColor = .clear - passwordTextField.backgroundColor = .clear - case .invalid: - let errorColor = UIColor.red.withAlphaComponent(0.3) - usernameTextField.backgroundColor = errorColor - passwordTextField.backgroundColor = errorColor - } - } - - enum CredentialsState { - case standard - case invalid - } -} - -extension AuthenticationBulletinPage: UITextFieldDelegate { - @objc open func isInputValid(text: String?) -> Bool { - if text == nil || text!.isEmpty { - return false - } - - return true - } - - func textFieldDidEndEditing(_ textField: UITextField) { - if isInputValid(text: textField.text) { - set(state: .standard) - } else { - set(state: .invalid) - } - } - - func textFieldShouldReturn(_ textField: UITextField) -> Bool { - if textField == usernameTextField { - passwordTextField.becomeFirstResponder() - } else if textField == self.passwordTextField { - actionButtonTapped(sender: UIButton()) - } - - return true - } -} diff --git a/App/Services/AuthenticationUIService/AuthenticationUIService.swift b/App/Services/AuthenticationUIService/AuthenticationUIService.swift deleted file mode 100644 index 7f7384de..00000000 --- a/App/Services/AuthenticationUIService/AuthenticationUIService.swift +++ /dev/null @@ -1,116 +0,0 @@ -// -// AuthenticationUIService.swift -// Hackers -// -// Created by Weiran Zhang on 27/05/2019. -// Copyright © 2019 Weiran Zhang. All rights reserved. -// - -import BLTNBoard -import PromiseKit - -class AuthenticationUIService { - private let sessionService: SessionService - - init(sessionService: SessionService) { - self.sessionService = sessionService - } - - var bulletinManager: BLTNItemManager? - - func showAuthentication() { - let manager = BLTNItemManager(rootItem: loginPage()) - manager.backgroundColor = AppTheme.default.backgroundColor - manager.backgroundViewStyle = .blurredDark - bulletinManager = manager - bulletinManager?.showBulletin(in: UIApplication.shared) - } - - private func displayActivityIndicator() { - guard let bulletinManager = self.bulletinManager else { return } - bulletinManager.displayActivityIndicator(color: AppTheme.default.textColor) - } - - private func loginPage() -> AuthenticationBulletinPage { - let page = AuthenticationBulletinPage(title: "Login") - page.descriptionText = "Hackers never stores your password." - page.actionButtonTitle = "Login" - if sessionService.authenticationState == .authenticated { - page.alternativeButtonTitle = "Logout" - } - page.isDismissable = true - - page.actionHandler = { item in - guard let item = item as? AuthenticationBulletinPage else { return } - - if item.usernameTextField.text!.isEmpty || item.passwordTextField.text!.isEmpty { - item.set(state: .invalid) - } else { - self.displayActivityIndicator() - - self.sessionService.authenticate(username: item.usernameTextField.text!, - password: item.passwordTextField.text!) - .done { _ in - page.next = self.loginSuccessPage() - item.manager?.displayNextItem() - }.ensure { - self.sendAuthenticationDidChangeNotification() - }.catch { _ in - item.set(state: .invalid) - self.bulletinManager?.hideActivityIndicator() - } - } - } - - page.alternativeHandler = { item in - HackersKit.shared.logout() - self.sendAuthenticationDidChangeNotification() - item.manager?.dismissBulletin() - } - - themeAppearance(of: page) - - return page - } - - private func loginSuccessPage() -> BLTNPageItem { - let page = BLTNPageItem(title: "Logged In") - let username = sessionService.username! - page.descriptionText = "Successfully logged in as \(username)" - page.image = UIImage(named: "SuccessIcon")?.withRenderingMode(.alwaysTemplate) - page.appearance.imageViewTintColor = #colorLiteral(red: 0.2980392157, green: 0.8509803922, blue: 0.3921568627, alpha: 1) - page.isDismissable = true - page.actionButtonTitle = "Dismiss" - page.actionHandler = { item in - item.manager?.dismissBulletin() - } - themeAppearance(of: page) - - return page - } - - private func themeAppearance(of item: BLTNPageItem) { - item.appearance.actionButtonColor = AppTheme.default.appTintColor - item.appearance.alternativeButtonTitleColor = AppTheme.default.appTintColor - item.appearance.titleTextColor = AppTheme.default.titleTextColor - item.appearance.descriptionTextColor = AppTheme.default.textColor - } - - private func sendAuthenticationDidChangeNotification() { - NotificationCenter.default.post(name: Notification.Name.refreshRequired, object: nil) - } - - func unauthenticatedAlertController() -> UIAlertController { - let unauthenticatedMessage = "You're not logged into Hacker News. Do you want to login now?" - let authenticationAlert = UIAlertController( - title: "Not logged in", - message: unauthenticatedMessage, - preferredStyle: .alert - ) - authenticationAlert.addAction(UIAlertAction(title: "Not Now", style: .cancel, handler: nil)) - authenticationAlert.addAction(UIAlertAction(title: "Login", style: .default, handler: { _ in - self.showAuthentication() - })) - return authenticationAlert - } -} diff --git a/App/Settings/LoginView.swift b/App/Settings/LoginView.swift index 1b92b772..d43b42a0 100644 --- a/App/Settings/LoginView.swift +++ b/App/Settings/LoginView.swift @@ -3,7 +3,7 @@ // Hackers // // Created by Weiran Zhang on 04/04/2022. -// Copyright © 2022 Glass Umbrella. All rights reserved. +// Copyright © 2022 Weiran Zhang. All rights reserved. // import SwiftUI @@ -22,55 +22,64 @@ struct LoginView: View { @Environment(\.presentationMode) var presentationMode var body: some View { - VStack { - Text("Login to Hacker News") - .font(.largeTitle) - .padding(.bottom, 30) - - TextField("Username", text: $username) - .autocapitalization(.none) - .disableAutocorrection(true) - .textFieldStyle(RoundedTextField()) - .textContentType(.username) - - SecureField("Password", text: $password) - .textFieldStyle(RoundedTextField()) - .textContentType(.password) - - Text("Hackers never stores your password") - .foregroundColor(Color.secondary) - .font(.footnote) - - Button("Login") { - isAuthenticating = true - sessionService.authenticate(username: username, password: password) - .done { _ in - presentationMode.wrappedValue.dismiss() - }.ensure { - NotificationCenter.default.post(name: Notification.Name.refreshRequired, object: nil) - isAuthenticating = false - }.catch { _ in - showAlert = true - password = "" + NavigationView { + VStack { + Text("Login to Hacker News") + .font(.largeTitle) + .padding(.bottom, 30) + + TextField("Username", text: $username) + .autocapitalization(.none) + .disableAutocorrection(true) + .textFieldStyle(RoundedTextField()) + .textContentType(.username) + + SecureField("Password", text: $password) + .textFieldStyle(RoundedTextField()) + .textContentType(.password) + + Text("Hackers never stores your password") + .foregroundColor(Color.secondary) + .font(.footnote) + + Button("Login") { + isAuthenticating = true + sessionService.authenticate(username: username, password: password) + .done { _ in + presentationMode.wrappedValue.dismiss() + }.ensure { + NotificationCenter.default.post(name: Notification.Name.refreshRequired, object: nil) + isAuthenticating = false + }.catch { _ in + showAlert = true + password = "" + } + }.buttonStyle(FilledButton()) + .padding(.top, 30) + .disabled(isAuthenticating) + .alert(isPresented: $showAlert) { + Alert( + title: Text("Login Failed"), + message: Text("Error logging into Hacker News, check your username and password.") + ) } - }.buttonStyle(FilledButton()) - .padding(.top, 30) - .disabled(isAuthenticating) - .alert(isPresented: $showAlert) { - Alert( - title: Text("Login Failed"), - message: Text("Error logging into Hacker News, check your username and password.") - ) - } - LabelledDivider(label: "or") + LabelledDivider(label: "or") - Link(destination: URL(string: "https://news.ycombinator.com/login")!) { - HStack { - Text("Register on Hacker News") - Image(systemName: "rectangle.portrait.and.arrow.right") + Link(destination: URL(string: "https://news.ycombinator.com/login")!) { + HStack { + Text("Register on Hacker News") + Image(systemName: "rectangle.portrait.and.arrow.right") + } + .padding() + } + } + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button("Done") { + presentationMode.wrappedValue.dismiss() + } } - .padding() } } } @@ -103,12 +112,11 @@ struct FilledButton: ButtonStyle { } struct LabelledDivider: View { - let label: String let horizontalPadding: CGFloat let color: Color - init(label: String, horizontalPadding: CGFloat = 20, color: Color = .gray) { + init(label: String, horizontalPadding: CGFloat = 20, color: Color = .secondary) { self.label = label self.horizontalPadding = horizontalPadding self.color = color @@ -127,35 +135,8 @@ struct LabelledDivider: View { } } -@propertyWrapper -struct Inject { - let wrappedValue: Component - init() { - self.wrappedValue = Resolver.shared.resolve(Component.self) - } -} - -class Resolver { - static let shared = Resolver() - private let container = buildContainer() - - func resolve(_ type: T.Type) -> T { - container.resolve(T.self)! - } -} - struct LoginView_Previews: PreviewProvider { static var previews: some View { LoginView() } } - -func buildContainer() -> Container { - let container = SwinjectStoryboard.defaultContainer - - container.register(SessionService.self) { _ in - return SessionService() - }.inObjectScope(.container) - - return container -} diff --git a/App/Settings/SettingsViewController.swift b/App/Settings/SettingsViewController.swift index d92fa71c..eff873e3 100644 --- a/App/Settings/SettingsViewController.swift +++ b/App/Settings/SettingsViewController.swift @@ -15,7 +15,6 @@ import Loaf class SettingsViewController: UITableViewController { var sessionService: SessionService? - var authenticationUIService: AuthenticationUIService? @IBOutlet weak var accountLabel: UILabel! @IBOutlet weak var usernameLabel: UILabel! @@ -145,11 +144,7 @@ extension SettingsViewController { } private func login() { - let loginView = LoginView() - let loginHC = UIHostingController(rootView: loginView) - present(loginHC, animated: true) - - //authenticationUIService?.showAuthentication() + AuthenticationHelper.showLoginView(self) } } diff --git a/App/Shared/SwipeCellKitActions.swift b/App/Shared/SwipeCellKitActions.swift index a8cfb899..5606772f 100644 --- a/App/Shared/SwipeCellKitActions.swift +++ b/App/Shared/SwipeCellKitActions.swift @@ -11,12 +11,6 @@ import SwipeCellKit import Loaf class SwipeCellKitActions { - private let authenticationUIService: AuthenticationUIService - - init(authenticationUIService: AuthenticationUIService) { - self.authenticationUIService = authenticationUIService - } - func voteAction( post: Post, tableView: UITableView, @@ -34,7 +28,10 @@ class SwipeCellKitActions { guard let error = error as? HackersKitError else { return } switch error { case .unauthenticated: - viewController.present(self.authenticationUIService.unauthenticatedAlertController(), animated: true) + viewController.present( + AuthenticationHelper.unauthenticatedAlertController(viewController), + animated: true + ) default: Loaf("Error connecting to Hacker News", state: .error, sender: viewController).show() } @@ -82,7 +79,10 @@ class SwipeCellKitActions { guard let error = error as? HackersKitError else { return } switch error { case .unauthenticated: - viewController.present(self.authenticationUIService.unauthenticatedAlertController(), animated: true) + viewController.present( + AuthenticationHelper.unauthenticatedAlertController(viewController), + animated: true + ) default: Loaf("Error connecting to Hacker News", state: .error, sender: viewController).show() } diff --git a/Hackers.xcodeproj/project.pbxproj b/Hackers.xcodeproj/project.pbxproj index 3b99dfcd..ed35852b 100644 --- a/Hackers.xcodeproj/project.pbxproj +++ b/Hackers.xcodeproj/project.pbxproj @@ -38,8 +38,8 @@ 2481BD63247C12540088CA2B /* HNScraperShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2481BD62247C12540088CA2B /* HNScraperShim.swift */; }; 2481BD65247C1B380088CA2B /* HackersKit+PostVoting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2481BD64247C1B380088CA2B /* HackersKit+PostVoting.swift */; }; 2486D7D527FAEBFC007504C6 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2486D7D427FAEBFC007504C6 /* LoginView.swift */; }; - 24993C55229C2BD400AF1891 /* AuthenticationUIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24993C54229C2BD400AF1891 /* AuthenticationUIService.swift */; }; - 24993C57229C768700AF1891 /* AuthenticationBulletinPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24993C56229C768700AF1891 /* AuthenticationBulletinPage.swift */; }; + 2486D7D727FB0A26007504C6 /* AuthenticationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2486D7D627FB0A26007504C6 /* AuthenticationHelper.swift */; }; + 2486D7D927FB0DC9007504C6 /* SwinjectSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2486D7D827FB0DC9007504C6 /* SwinjectSwiftUI.swift */; }; 249A7E32194374550059C079 /* CommentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249A7E31194374550059C079 /* CommentTableViewCell.swift */; }; 249B8F8322A2A48B002A9EC5 /* NotificationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249B8F8222A2A48B002A9EC5 /* NotificationToken.swift */; }; 249B8F8522A2A519002A9EC5 /* NotificationCenterExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249B8F8422A2A519002A9EC5 /* NotificationCenterExtensions.swift */; }; @@ -162,8 +162,8 @@ 2481BD62247C12540088CA2B /* HNScraperShim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HNScraperShim.swift; sourceTree = ""; }; 2481BD64247C1B380088CA2B /* HackersKit+PostVoting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HackersKit+PostVoting.swift"; sourceTree = ""; }; 2486D7D427FAEBFC007504C6 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; - 24993C54229C2BD400AF1891 /* AuthenticationUIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationUIService.swift; sourceTree = ""; }; - 24993C56229C768700AF1891 /* AuthenticationBulletinPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationBulletinPage.swift; sourceTree = ""; }; + 2486D7D627FB0A26007504C6 /* AuthenticationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationHelper.swift; sourceTree = ""; }; + 2486D7D827FB0DC9007504C6 /* SwinjectSwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwinjectSwiftUI.swift; sourceTree = ""; }; 249A7E31194374550059C079 /* CommentTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentTableViewCell.swift; sourceTree = ""; }; 249B8F8222A2A48B002A9EC5 /* NotificationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationToken.swift; sourceTree = ""; }; 249B8F8422A2A519002A9EC5 /* NotificationCenterExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterExtensions.swift; sourceTree = ""; }; @@ -341,6 +341,7 @@ isa = PBXGroup; children = ( 24FF021C226C9BEA002EF8DD /* SwinjectStoryboardExtensions.swift */, + 2486D7D827FB0DC9007504C6 /* SwinjectSwiftUI.swift */, 24CEAA9A1F8916970050DEDF /* UIViewExtensions.swift */, 2464D212209DEFB100C7F8FC /* UINavigationControllerExtensions.swift */, 243271941F95EDA9008196DB /* StringExtensions.swift */, @@ -366,15 +367,6 @@ path = Settings; sourceTree = ""; }; - 24993C58229C76EA00AF1891 /* AuthenticationUIService */ = { - isa = PBXGroup; - children = ( - 24993C54229C2BD400AF1891 /* AuthenticationUIService.swift */, - 24993C56229C768700AF1891 /* AuthenticationBulletinPage.swift */, - ); - path = AuthenticationUIService; - sourceTree = ""; - }; 249B8F8122A2A474002A9EC5 /* Shared */ = { isa = PBXGroup; children = ( @@ -532,10 +524,10 @@ 24FF0219226C99B1002EF8DD /* Services */ = { isa = PBXGroup; children = ( - 24993C58229C76EA00AF1891 /* AuthenticationUIService */, 2477BDE4227D9D8200771FE2 /* SessionService.swift */, 24B1A6A422B508A40051EAFD /* OnboardingService.swift */, 247C9C78248BC35000AF4E60 /* NavigationService.swift */, + 2486D7D627FB0A26007504C6 /* AuthenticationHelper.swift */, ); path = Services; sourceTree = ""; @@ -930,7 +922,6 @@ 2481BD5B247C0FFB0088CA2B /* Models.swift in Sources */, 249A7E32194374550059C079 /* CommentTableViewCell.swift in Sources */, 24CE5FED19434BDC009D2677 /* AppDelegate.swift in Sources */, - 24993C57229C768700AF1891 /* AuthenticationBulletinPage.swift in Sources */, 24E390AA2542D7A9000DA7A8 /* UIViewControllerExtensions.swift in Sources */, 24E4F26119B4DA9900574B52 /* CommentDelegate.swift in Sources */, 2479E11522B69CF100D9F1A4 /* ThumbnailImageView.swift in Sources */, @@ -948,16 +939,17 @@ 249B8F8722A2B740002A9EC5 /* SwipeCellKitActions.swift in Sources */, 24191F8D1944C991003C0D98 /* CommentsController.swift in Sources */, 24CEAA9D1F8917D60050DEDF /* ReviewController.swift in Sources */, - 24993C55229C2BD400AF1891 /* AuthenticationUIService.swift in Sources */, 2464D213209DEFB100C7F8FC /* UINavigationControllerExtensions.swift in Sources */, 24B1A6A522B508A40051EAFD /* OnboardingService.swift in Sources */, 249B8F8A22A2C9B3002A9EC5 /* TableViewBackgroundView.swift in Sources */, + 2486D7D727FB0A26007504C6 /* AuthenticationHelper.swift in Sources */, 249B8F8322A2A48B002A9EC5 /* NotificationToken.swift in Sources */, 2481BD61247C11AE0088CA2B /* HackersKit+CommentVoting.swift in Sources */, 24D330CA248BAC4C00A2AA10 /* HackersKit+Authentication.swift in Sources */, 24C20D391A7E42D5005B759B /* MainSplitViewController.swift in Sources */, 2464D21F209E27FE00C7F8FC /* UserDefaultsExtensions.swift in Sources */, 24CE5FF819435A90009D2677 /* PostCell.swift in Sources */, + 2486D7D927FB0DC9007504C6 /* SwinjectSwiftUI.swift in Sources */, 24B982DF22BE65FF000D8913 /* SettingsStore.swift in Sources */, 243C182B2780A8370018359D /* CollectionExtensions.swift in Sources */, 24628BAD21E6897700BDEEF9 /* AppFont.swift in Sources */, From d8b6c302bf53f2e9344e97d9074e3cfb525337b3 Mon Sep 17 00:00:00 2001 From: Weiran Zhang Date: Mon, 4 Apr 2022 13:03:08 +0100 Subject: [PATCH 4/8] Remove BulletinBoard --- Hackers.xcodeproj/project.pbxproj | 2 -- Podfile | 1 - Podfile.lock | 6 +----- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/Hackers.xcodeproj/project.pbxproj b/Hackers.xcodeproj/project.pbxproj index ed35852b..7dc1ab22 100644 --- a/Hackers.xcodeproj/project.pbxproj +++ b/Hackers.xcodeproj/project.pbxproj @@ -838,7 +838,6 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Hackers/Pods-Hackers-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/BulletinBoard/BLTNBoard.framework", "${BUILT_PRODUCTS_DIR}/HNScraper/HNScraper.framework", "${BUILT_PRODUCTS_DIR}/Loaf/Loaf.framework", "${BUILT_PRODUCTS_DIR}/Nuke/Nuke.framework", @@ -851,7 +850,6 @@ ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BLTNBoard.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HNScraper.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Loaf.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nuke.framework", diff --git a/Podfile b/Podfile index cab018cf..abd0e1f8 100644 --- a/Podfile +++ b/Podfile @@ -10,7 +10,6 @@ target 'Hackers' do pod 'SwipeCellKit' pod 'Swinject' pod 'SwinjectStoryboard' - pod 'BulletinBoard' pod 'WhatsNewKit' pod 'SwiftSoup' pod 'Nuke' diff --git a/Podfile.lock b/Podfile.lock index c948afb8..7c215feb 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,5 +1,4 @@ PODS: - - BulletinBoard (5.0.0) - DeviceKit (4.6.0) - HNScraper (0.2.2) - Loaf (0.7.0) @@ -14,7 +13,6 @@ PODS: - WhatsNewKit (1.3.7) DEPENDENCIES: - - BulletinBoard - DeviceKit - HNScraper (from `https://github.com/weiran/HNScraper.git`, branch `master`) - Loaf @@ -29,7 +27,6 @@ DEPENDENCIES: SPEC REPOS: trunk: - - BulletinBoard - DeviceKit - Loaf - Nuke @@ -52,7 +49,6 @@ CHECKOUT OPTIONS: :git: https://github.com/weiran/HNScraper.git SPEC CHECKSUMS: - BulletinBoard: dba77053c2a7b64953f63073f7534165af2edd21 DeviceKit: 834dd8aa1be53cd85364d8f49aa5d98bfac397c4 HNScraper: d96632c361238905f2886f83675e75f2153b4dc4 Loaf: d69937cd00649f3fa260beb8b7aef0a1feb861ef @@ -65,6 +61,6 @@ SPEC CHECKSUMS: SwipeCellKit: 3972254a826da74609926daf59b08d6c72e619ea WhatsNewKit: c87028c4059dccd113495422801914cc53f6aab0 -PODFILE CHECKSUM: 01446ada016a8c363277039b82b7b1c2962beffe +PODFILE CHECKSUM: f1f466d6ff3dd6852b57d4996e0d211a235b9113 COCOAPODS: 1.11.3 From 5b3c046d0cc8cf2a144967ef56801ad22c2fb99e Mon Sep 17 00:00:00 2001 From: Weiran Zhang Date: Mon, 4 Apr 2022 13:03:17 +0100 Subject: [PATCH 5/8] Bold done button --- App/Settings/LoginView.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/App/Settings/LoginView.swift b/App/Settings/LoginView.swift index d43b42a0..8c6af7c2 100644 --- a/App/Settings/LoginView.swift +++ b/App/Settings/LoginView.swift @@ -76,8 +76,11 @@ struct LoginView: View { } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { - Button("Done") { + Button { presentationMode.wrappedValue.dismiss() + } label: { + Text("Done") + .bold() } } } From 9eac352393b27b2691c1741db147b005001703e2 Mon Sep 17 00:00:00 2001 From: Weiran Zhang Date: Mon, 4 Apr 2022 13:03:28 +0100 Subject: [PATCH 6/8] New login onboarding --- App/Services/OnboardingService.swift | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/App/Services/OnboardingService.swift b/App/Services/OnboardingService.swift index c43a5139..c443dd5d 100644 --- a/App/Services/OnboardingService.swift +++ b/App/Services/OnboardingService.swift @@ -11,9 +11,6 @@ import WhatsNewKit enum OnboardingService { static func onboardingViewController(forceShow: Bool = false) -> UIViewController? { - // disable "whats new" for bugfix releases - return nil - if ProcessInfo.processInfo.arguments.contains("disableOnboarding"), forceShow == false { return nil } @@ -61,22 +58,12 @@ enum OnboardingService { } private static func items() -> [WhatsNew.Item] { - let pullToRefreshItem = WhatsNew.Item( - title: "Pull to refresh comments", - subtitle: "Thanks @ballwood.", - image: UIImage(systemName: "arrow.clockwise") - ) - 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") + let loginExperienceItem = WhatsNew.Item( + title: "New Login", + subtitle: "Improved login experience with password manager support.", + image: UIImage(systemName: "lock.shield.fill") ) - return [pullToRefreshItem, shareCommentItem, bugfixesItem] + return [loginExperienceItem] } } @@ -87,7 +74,7 @@ struct OnboardingViewControllerWrapper: UIViewControllerRepresentable { context: UIViewControllerRepresentableContext ) -> UIViewControllerType { let onboardingViewController = OnboardingService.onboardingViewController(forceShow: true)! - // swiftlint:disable force_cast + // swiftlint:disable:next force_cast return onboardingViewController as! UIViewControllerType } From 9d7d4da7c374cc871868cbf24c8b3ff5d7fb9443 Mon Sep 17 00:00:00 2001 From: Weiran Zhang Date: Mon, 4 Apr 2022 13:11:06 +0100 Subject: [PATCH 7/8] Version 4.7, build 110 --- Extensions/HackersActionExtension/Info.plist | 4 ++-- Extensions/HackersShareExtension/Info.plist | 4 ++-- Hackers.xcodeproj/project.pbxproj | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Extensions/HackersActionExtension/Info.plist b/Extensions/HackersActionExtension/Info.plist index 34935cc0..bcc5658b 100644 --- a/Extensions/HackersActionExtension/Info.plist +++ b/Extensions/HackersActionExtension/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 4.6.7 + 4.7 CFBundleVersion - 109 + 110 NSExtension NSExtensionAttributes diff --git a/Extensions/HackersShareExtension/Info.plist b/Extensions/HackersShareExtension/Info.plist index 168f12d2..a9447562 100644 --- a/Extensions/HackersShareExtension/Info.plist +++ b/Extensions/HackersShareExtension/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 4.6.7 + 4.7 CFBundleVersion - 109 + 110 NSExtension NSExtensionAttributes diff --git a/Hackers.xcodeproj/project.pbxproj b/Hackers.xcodeproj/project.pbxproj index 7dc1ab22..5d55185b 100644 --- a/Hackers.xcodeproj/project.pbxproj +++ b/Hackers.xcodeproj/project.pbxproj @@ -1318,7 +1318,7 @@ CODE_SIGN_ENTITLEMENTS = "App/Supporting Files/Hackers.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 109; + CURRENT_PROJECT_VERSION = 110; DEVELOPMENT_TEAM = 2KB59GPA9B; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -1326,7 +1326,7 @@ INFOPLIST_FILE = "App/Supporting Files/Hackers-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 4.6.7; + MARKETING_VERSION = 4.7; PRODUCT_BUNDLE_IDENTIFIER = "com.weiranzhang.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Hackers; PROVISIONING_PROFILE_SPECIFIER = "Hackers Dev Profile"; @@ -1348,7 +1348,7 @@ CODE_SIGN_ENTITLEMENTS = "App/Supporting Files/Hackers.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 109; + CURRENT_PROJECT_VERSION = 110; DEVELOPMENT_TEAM = 2KB59GPA9B; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -1356,7 +1356,7 @@ INFOPLIST_FILE = "App/Supporting Files/Hackers-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 4.6.7; + MARKETING_VERSION = 4.7; PRODUCT_BUNDLE_IDENTIFIER = "com.weiranzhang.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Hackers; PROVISIONING_PROFILE_SPECIFIER = "Hackers Prod Profile"; From 4048c17533e135f7c567ed29ce3e168456d3f876 Mon Sep 17 00:00:00 2001 From: Weiran Zhang Date: Mon, 4 Apr 2022 13:34:29 +0100 Subject: [PATCH 8/8] Fix UI tests --- App/Main.storyboard | 58 +++++++++++++---------- Tests/HackersUITests/HackersUITests.swift | 32 ++++++++----- 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/App/Main.storyboard b/App/Main.storyboard index 31c6d9a9..e8ee841e 100644 --- a/App/Main.storyboard +++ b/App/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -64,7 +64,7 @@ - + @@ -150,7 +150,7 @@ - + @@ -221,6 +221,9 @@ + + + @@ -231,7 +234,7 @@ - + @@ -286,6 +289,9 @@ + + + @@ -327,7 +333,7 @@ - + - + - +