// // KMSignUpView.swift // PDF Reader Pro // // Created by wanjun on 2024/10/23. // import Cocoa import Combine class KMSignUpView: KMBaseXibView { @IBOutlet weak var signUpLabel: NSTextField! @IBOutlet weak var loginModeBox: NSBox! @IBOutlet weak var verificationCodeButton: NSButton! @IBOutlet weak var selectBox1: NSBox! @IBOutlet weak var passwordButton: NSButton! @IBOutlet weak var selectBox2: NSBox! @IBOutlet weak var emailBox: NSBox! @IBOutlet weak var emailTextField: NSTextField! @IBOutlet weak var emailErrorLabel: NSTextField! @IBOutlet weak var passwordBox: NSBox! @IBOutlet weak var verifficationView: NSView! @IBOutlet weak var verifficationBox: NSBox! @IBOutlet weak var verifficationTextField: NSTextField! @IBOutlet weak var sendBox: KMBox! @IBOutlet weak var sendLabel: NSTextField! @IBOutlet weak var passwordView: NSView! @IBOutlet weak var passwordTextField: NSTextField! @IBOutlet weak var passwordTextField1: NSSecureTextField! @IBOutlet weak var visibleButton: NSButton! @IBOutlet weak var passwordErrorLabel: NSTextField! @IBOutlet weak var stayCheckButton: NSButton! @IBOutlet weak var stayLabel: NSTextField! @IBOutlet weak var forgetButton: NSButton! @IBOutlet weak var signUpBox: NSBox! @IBOutlet weak var signUpButton: NSButton! @IBOutlet weak var privacyCheckButton: NSButton! @IBOutlet weak var privacyLabel: NSTextField! private var viewModel = KMSignUpViewModel() private var cancellables = Set() convenience init(model: KMSignUpViewModel, superView: NSView) { self.init(frame: superView.bounds) viewModel = model bindViewModel() languageLocalized() initializeUI() } public override init(frame frameRect: NSRect) { super.init(frame: frameRect) } public required init?(coder decoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func updateUI() { super.updateUI() bindViewModel() languageLocalized() initializeUI() } // MARK: Private Method private func languageLocalized() -> Void { signUpLabel.stringValue = NSLocalizedString("Sign Up", tableName: "MemberCenterLocalizable", comment: "") verificationCodeButton.title = NSLocalizedString("Verification code", tableName: "MemberCenterLocalizable", comment: "") passwordButton.title = NSLocalizedString("Password", tableName: "MemberCenterLocalizable", comment: "") stayLabel.stringValue = NSLocalizedString("Stay logged in", tableName: "MemberCenterLocalizable", comment: "") forgetButton.title = NSLocalizedString("Forget Password?", tableName: "MemberCenterLocalizable", comment: "") signUpButton.title = NSLocalizedString("Sign Up", tableName: "MemberCenterLocalizable", comment: "") emailErrorLabel.stringValue = String(format: "*%@", NSLocalizedString("Please enter right Address", tableName: "MemberCenterLocalizable", comment: "")) passwordErrorLabel.stringValue = String(format: "*%@", NSLocalizedString("Please enter right Verification code", tableName: "MemberCenterLocalizable", comment: "")) emailTextField.placeholderString = NSLocalizedString("Please enter Email", tableName: "MemberCenterLocalizable", comment: "") verifficationTextField.placeholderString = NSLocalizedString("Enter Verification code", tableName: "MemberCenterLocalizable", comment: "") passwordTextField.placeholderString = NSLocalizedString("Password", tableName: "MemberCenterLocalizable", comment: "") passwordTextField1.placeholderString = NSLocalizedString("Password", tableName: "MemberCenterLocalizable", comment: "") privacyCheckButton.toolTip = NSLocalizedString("Please first agree and check the above agreement.", tableName: "MemberCenterLocalizable", comment: "") emailTextField.stringValue = viewModel.email verifficationTextField.stringValue = viewModel.verificationCode passwordTextField.stringValue = viewModel.password passwordTextField1.stringValue = viewModel.password } private func initializeUI() -> Void { emailTextField.delegate = self verifficationTextField.delegate = self passwordTextField.delegate = self passwordTextField1.delegate = self emailErrorLabel.isHidden = !viewModel.emailError() passwordErrorLabel.isHidden = !viewModel.passwordError() signUpLabel.textColor = NSColor(named: "000000") signUpLabel.font = NSFont.SFMediumFontWithSize(20) selectBox1.fillColor = NSColor(named: "4982E6") ?? NSColor.blue emailErrorLabel.textColor = NSColor(named: "FA1E5D") emailErrorLabel.font = NSFont.SFProTextRegularFont(9) passwordBox.borderColor = NSColor(named: "DADBDE") ?? NSColor.gray verifficationBox.borderColor = NSColor(named: "FA1E5D") ?? NSColor.gray if viewModel.isValidEmail() { sendBox.fillColor = NSColor(named: "273C62") ?? NSColor.blue } else { sendBox.fillColor = NSColor(named: "273C62_0.4") ?? NSColor.blue } sendBox.borderColor = NSColor(named: "273C62") ?? NSColor.blue sendLabel.textColor = NSColor(named: "FFFFFF") ?? NSColor.white sendLabel.font = NSFont.SFProTextRegularFont(16) passwordErrorLabel.textColor = NSColor(named: "FA1E5D") passwordErrorLabel.font = NSFont.SFProTextRegularFont(9) stayCheckButton.image = NSImage(named: "CheckBoxNor") stayLabel.textColor = NSColor(named: "0E1114") ?? NSColor.black stayLabel.font = NSFont.SFProTextRegularFont(12) forgetButton.setTitleColor(color: NSColor(named: "4982E6") ?? NSColor.blue, font: NSFont.SFProTextRegularFont(12)) signUpBox.fillColor = NSColor(named: "273C62") ?? NSColor.blue signUpButton.setTitleColor(color: NSColor(named: "FFFFFF") ?? NSColor.white, font: NSFont.SFProTextRegularFont(16)) privacyCheckButton.image = NSImage(named: "CheckBoxNor") privacyLabel.isEditable = false privacyLabel.isSelectable = true privacyLabel.allowsEditingTextAttributes = true privacyLabel.textColor = NSColor.black privacyLabel.font = NSFont.SFProTextRegularFont(16.0) let tipsString = NSLocalizedString("I have read and agree to the %@ and %@, and an account will be automatically created after I log in to an unregistered email address", comment: "") let specialOffer = NSLocalizedString("User Agreement", comment: "") let contactsUs = NSLocalizedString("Privacy Policy", comment: "") let fullString = String(format: tipsString, specialOffer, contactsUs) let attributedString = NSMutableAttributedString(string: fullString) // 定义链接的范围 let specialOfferRange = (fullString as NSString).range(of: specialOffer) let contactsUsRange = (fullString as NSString).range(of: contactsUs) let linkColor = NSColor(named: "4982E6") ?? NSColor.blue let font = NSFont.SFProTextRegularFont(11.0) // 与普通文本相同的字体 attributedString.addAttributes([ .foregroundColor: NSColor(named: "0E1114") ?? NSColor.black as Any, .font: font ], range: (fullString as NSString).range(of: fullString)) attributedString.addAttributes([ .foregroundColor: linkColor, .link: NSLocalizedString("https://www.pdfreaderpro.com/terms_of_service", comment: ""), .font: font ], range: specialOfferRange) attributedString.addAttributes([ .foregroundColor: linkColor, .link: NSLocalizedString("https://www.pdfreaderpro.com/privacy-policy", comment: ""), .font: font ], range: contactsUsRange) privacyLabel.attributedStringValue = attributedString signUpStateChange() visibleStateChange() textfieldInputState(isEmail: true) textfieldInputState(isEmail: false) sendBoxRefresh() sendBox.moveCallback = { [weak self](mouseEntered: Bool, mouseBox: KMBox) -> Void in guard let self = self else { return } if self.viewModel.email.count <= 0 { return } if self.viewModel.sendBoxSelect { return } if !self.viewModel.isValidEmail() { return } if mouseEntered { self.sendBox.fillColor = NSColor(named: "000000_0.1") ?? NSColor.blue self.sendBox.borderWidth = 1 } else { self.sendBox.fillColor = NSColor(named: "273C62") ?? NSColor.blue } } sendBox.downCallback = { [weak self](downEntered: Bool, mouseBox: KMBox, event) -> Void in guard let self = self else { return } if self.viewModel.email.count <= 0 { return } if self.viewModel.sendBoxSelect { return } if downEntered { self.sendBox.fillColor = NSColor(named: "273C62_0.4") ?? NSColor.blue viewModel.countDown(type: .login) } } } private func signUpStateChange() -> Void { if viewModel.signUpState == .verificationCode { selectBox1.isHidden = false selectBox2.isHidden = true verificationCodeButton.setTitleColor(color: NSColor(named: "4982E6") ?? NSColor.black, font: NSFont.SFProTextRegularFont(14)) passwordButton.setTitleColor(color: NSColor(named: "42464D") ?? NSColor.black, font: NSFont.SFProTextRegularFont(14)) verifficationView.isHidden = false passwordView.isHidden = true verifficationTextField.placeholderString = NSLocalizedString("Enter Verification code", tableName: "MemberCenterLocalizable", comment: "") forgetButton.isHidden = true } else if viewModel.signUpState == .password { selectBox1.isHidden = true selectBox2.isHidden = false verificationCodeButton.setTitleColor(color: NSColor(named: "42464D") ?? NSColor.black, font: NSFont.SFProTextRegularFont(14)) passwordButton.setTitleColor(color: NSColor(named: "4982E6") ?? NSColor.black, font: NSFont.SFProTextRegularFont(14)) verifficationView.isHidden = true passwordView.isHidden = false passwordTextField.placeholderString = NSLocalizedString("Password", tableName: "MemberCenterLocalizable", comment: "") passwordTextField1.placeholderString = NSLocalizedString("Password", tableName: "MemberCenterLocalizable", comment: "") forgetButton.isHidden = false } } private func checkStateChange(button: NSButton!, state: Bool) -> Void { button.state = state ? .on : .off if button.state == .on { button.image = NSImage(named: "CheckBoxSel") } else { button.image = NSImage(named: "CheckBoxNor") } } private func visibleStateChange() -> Void { if viewModel.isVisible { visibleButton.image = NSImage(named: "passwordUnVisible") passwordTextField.isHidden = false passwordTextField1.isHidden = true passwordTextField.stringValue = viewModel.password } else { visibleButton.image = NSImage(named: "passwordVisible") passwordTextField.isHidden = true passwordTextField1.isHidden = false passwordTextField1.stringValue = viewModel.password } } private func textfieldInputState(isEmail: Bool) -> Void { if isEmail { if viewModel.emailError() { emailBox.borderColor = NSColor(named: "FA1E5D") ?? NSColor.red } else { emailBox.borderColor = NSColor(named: "DADBDE") ?? NSColor.gray } emailErrorLabel.isHidden = !viewModel.emailError() } else { if viewModel.passwordError() { if viewModel.signUpState == .verificationCode { passwordBox.borderWidth = 0 verifficationBox.borderWidth = 1 } else if viewModel.signUpState == .password { passwordBox.borderWidth = 1 verifficationBox.borderWidth = 0 } } else { if viewModel.signUpState == .verificationCode { passwordBox.borderWidth = 1 verifficationBox.borderWidth = 0 } else if viewModel.signUpState == .password { passwordBox.borderWidth = 1 verifficationBox.borderWidth = 0 } } passwordErrorLabel.isHidden = !viewModel.passwordError() } } private func sendBoxRefresh() -> Void { sendLabel.stringValue = viewModel.sendContent if viewModel.sendContent == NSLocalizedString("Send", tableName: "MemberCenterLocalizable", comment: "") || viewModel.sendContent == NSLocalizedString("Resend", tableName: "MemberCenterLocalizable", comment: "") { if viewModel.email.count > 0 { if viewModel.isValidEmail() { sendBox.fillColor = NSColor(named: "273C62") ?? NSColor.blue } else { sendBox.fillColor = NSColor(named: "273C62_0.4") ?? NSColor.blue } } else { sendBox.fillColor = NSColor(named: "273C62_0.4") ?? NSColor.blue } } else { sendBox.fillColor = NSColor(named: "DADBDE") ?? NSColor.gray sendLabel.stringValue = String(format: "%@s", viewModel.sendContent) } } // MARK: Bind Method func bindViewModel() -> Void { viewModel.$isVisible .receive(on: RunLoop.main) .sink { [weak self] newValue in self?.visibleStateChange() } .store(in: &cancellables) viewModel.$stayState .receive(on: RunLoop.main) .sink { [weak self] newValue in self?.checkStateChange(button: self?.stayCheckButton, state: newValue) } .store(in: &cancellables) viewModel.$privacyState .receive(on: RunLoop.main) .sink { [weak self] newValue in self?.checkStateChange(button: self?.privacyCheckButton, state: newValue) } .store(in: &cancellables) viewModel.$signUpState .receive(on: RunLoop.main) .sink { [weak self] newValue in self?.signUpStateChange() } .store(in: &cancellables) viewModel.$emailErrorMessage .receive(on: RunLoop.main) .sink { [weak self] newValue in self?.emailErrorLabel.stringValue = newValue self?.emailErrorLabel.isHidden = false } .store(in: &cancellables) viewModel.$passwordErrorMessage .receive(on: RunLoop.main) .sink { [weak self] newValue in self?.passwordErrorLabel.stringValue = newValue self?.passwordErrorLabel.isHidden = false } .store(in: &cancellables) viewModel.$sendContent .receive(on: RunLoop.main) .sink { [weak self] newValue in self?.sendBoxRefresh() } .store(in: &cancellables) viewModel.$email .receive(on: RunLoop.main) .sink { [weak self] newValue in self?.sendBoxRefresh() } .store(in: &cancellables) viewModel.$verificationCode .receive(on: RunLoop.main) .sink { [weak self] newValue in if newValue.count <= 6 && newValue.count >= 0 { self?.viewModel.passwordErrorMessage = "" } else { self?.viewModel.passwordErrorMessage = NSLocalizedString("*Please enter right Verification code", tableName: "MemberCenterLocalizable", comment: "") } } .store(in: &cancellables) } // MARK: Action Method @IBAction func verificationCodeAction(_ sender: NSButton) { viewModel.signUpStateChange(state: .verificationCode) } @IBAction func passwordAction(_ sender: NSButton) { viewModel.signUpStateChange(state: .password) } @IBAction func visibleAction(_ sender: NSButton) { viewModel.isVisible.toggle() } @IBAction func stayCheckAction(_ sender: NSButton) { viewModel.stayState.toggle() } @IBAction func signUpAction(_ sender: NSButton) { viewModel.emailErrorMessage = "" viewModel.passwordErrorMessage = "" viewModel.signUpAction() } @IBAction func forgetAction(_ sender: NSButton) { guard let parentView = self.superview else { return } if parentView is NSBox { let model = KMSignUpViewModel() model.email = viewModel.email let forgotView = KMForgotPasswordView(model: model, superView: parentView) NSAnimationContext.runAnimationGroup { context in context.duration = 0.3 self.animator().alphaValue = 0 } completionHandler: { self.removeFromSuperview() forgotView.alphaValue = 0 (parentView as! NSBox).contentView = forgotView NSAnimationContext.runAnimationGroup({ context in context.duration = 0.3 forgotView.animator().alphaValue = 1 }, completionHandler: nil) } } else { let model = KMSignUpViewModel() model.email = viewModel.email let forgotView = KMForgotPasswordView(model: model, superView: parentView) NSAnimationContext.runAnimationGroup { context in context.duration = 0.3 self.animator().alphaValue = 0 } completionHandler: { self.removeFromSuperview() forgotView.alphaValue = 0 parentView.addSubview(forgotView) NSAnimationContext.runAnimationGroup({ context in context.duration = 0.3 forgotView.animator().alphaValue = 1 }, completionHandler: nil) } } } @IBAction func privacyCheckAction(_ sender: NSButton) { viewModel.privacyState.toggle() } } extension KMSignUpView: NSTextFieldDelegate { func controlTextDidEndEditing(_ obj: Notification) { let textField = obj.object as? NSTextField if textField == emailTextField { viewModel.email = textField!.stringValue } else if textField == verifficationTextField { viewModel.verificationCode = textField!.stringValue } else if textField == passwordTextField { viewModel.password = textField!.stringValue } else if textField == passwordTextField1 { viewModel.password = textField!.stringValue } } func controlTextDidChange(_ obj: Notification) { let textField = obj.object as? NSTextField if textField == emailTextField { viewModel.email = textField!.stringValue } else if textField == verifficationTextField { viewModel.verificationCode = textField!.stringValue } else if textField == passwordTextField { viewModel.password = textField!.stringValue } else if textField == passwordTextField1 { viewModel.password = textField!.stringValue } } }