KMVerificationCodeView.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. //
  2. // KMVerificationCodeView.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by lizhe on 2023/2/23.
  6. //
  7. import Cocoa
  8. typealias KMVerificationCodeViewCancelAction = (_ view: KMVerificationCodeView) -> Void
  9. typealias KMVerificationCodeViewVerificationCodeAction = (_ view: KMVerificationCodeView, _ data: KMRegisterModel, _ codeString: String) -> Void
  10. typealias KMVerificationCodeViewDoneAction = (_ view: KMVerificationCodeView, _ data: KMRegisterModel, _ sender: NSButton) -> Void
  11. typealias KMVerificationCodeViewCloseAction = (_ view: KMVerificationCodeView) -> Void
  12. typealias KMVerificationCodeViewReSendAction = (_ view: KMVerificationCodeView, _ sender: NSTextView) -> Void
  13. class KMVerificationCodeView: BaseXibView {
  14. @IBOutlet weak var titleLabel: NSTextField!
  15. @IBOutlet weak var describeLabel: NSTextField!
  16. @IBOutlet var timerTextView: NSTextView!
  17. @IBOutlet weak var cancellationButton: NSButton!
  18. @IBOutlet weak var cancelButton: NSButton!
  19. @IBOutlet weak var closeButton: NSButton!
  20. @IBOutlet weak var textFieldContentView: NSView!
  21. @IBOutlet weak var code1ContentView: NSView!
  22. @IBOutlet weak var code1TextField: KMBaseTextField!
  23. @IBOutlet weak var code2ContentView: NSView!
  24. @IBOutlet weak var code2TextField: KMBaseTextField!
  25. @IBOutlet weak var code3ContentView: NSView!
  26. @IBOutlet weak var code3TextField: KMBaseTextField!
  27. @IBOutlet weak var code4ContentView: NSView!
  28. @IBOutlet weak var code4TextField: KMBaseTextField!
  29. @IBOutlet weak var code5ContentView: NSView!
  30. @IBOutlet weak var code5TextField: KMBaseTextField!
  31. @IBOutlet weak var code6ContentView: NSView!
  32. @IBOutlet weak var code6TextField: KMBaseTextField!
  33. @IBOutlet weak var doneButtonTopConstraint: NSLayoutConstraint!
  34. @IBOutlet weak var alertView: KMLightMemberAlertView!
  35. @IBOutlet weak var alertHeightConstraint: NSLayoutConstraint!
  36. @IBOutlet weak var closeBox: KMBox!
  37. var cancellationButtonVC: KMDesignButton!
  38. var cancelButtonVC: KMDesignButton!
  39. var cancelAction: KMVerificationCodeViewCancelAction?
  40. var doneAction: KMVerificationCodeViewDoneAction?
  41. var closeAction: KMVerificationCodeViewCloseAction?
  42. var reSendAction: KMVerificationCodeViewReSendAction?
  43. var verificationCodeAction: KMVerificationCodeViewVerificationCodeAction?
  44. var timer: Timer?
  45. var time: Int = -1
  46. let startTime = 60
  47. //验证码
  48. var verificationCode: String {
  49. get {
  50. return self.code1TextField.textField.stringValue + self.code2TextField.textField.stringValue + self.code3TextField.textField.stringValue + self.code4TextField.textField.stringValue + self.code5TextField.textField.stringValue + self.code6TextField.textField.stringValue
  51. }
  52. }
  53. var verificationCodeState: KMRequestServerErrorCodeType = .unknown
  54. var inputType: KMRegisterLogType = .login {
  55. didSet {
  56. switch self.inputType {
  57. case .register:
  58. self.verifyCodeType = .register
  59. case .loginInputPassword:
  60. self.verifyCodeType = .resetPassword
  61. case .accountInfo:
  62. self.verifyCodeType = .logOff
  63. default:
  64. KMPrint("")
  65. }
  66. self.cleanVerificationCode()
  67. self.reloadData()
  68. self.updateLanguage()
  69. }
  70. }
  71. var verifyCodeType: KMVerifyCodeType = .unknown {
  72. didSet {
  73. self.reloadData()
  74. self.updateLanguage()
  75. }
  76. }
  77. var isNetworking: Bool = false //是否正在进行网络请求
  78. var codeIsTrue: Bool = false //验证码是否正确
  79. var model: KMRegisterModel = KMRegisterModel()
  80. deinit {
  81. self.endTimer()
  82. KMPrint("KMVerificationCodeView dealloc")
  83. }
  84. override func draw(_ dirtyRect: NSRect) {
  85. super.draw(dirtyRect)
  86. // Drawing code here.
  87. }
  88. func setup() {
  89. self.backgroundColor(NSColor.km_init(hex: "#FFFFFF"))
  90. self.titleLabel.font = NSFont.SFProTextSemiboldFont(20.0)
  91. self.titleLabel.textColor = NSColor.km_init(hex: "#252629")
  92. self.describeLabel.font = NSFont.SFProTextRegularFont(14.0)
  93. self.describeLabel.textColor = NSColor.km_init(hex: "#252629")
  94. self.timerTextView.delegate = self
  95. self.timerTextView.frame = (self.timerTextView.enclosingScrollView?.contentView.bounds)!
  96. self.timerTextView.autoresizingMask = [.width, .height]
  97. let textFieldArray = [self.code1TextField,self.code2TextField,self.code3TextField,self.code4TextField,self.code5TextField,self.code6TextField]
  98. for i in 0...textFieldArray.count - 1 {
  99. let textField = textFieldArray[i]
  100. if textField != nil {
  101. textField!.textField.textColor = NSColor.km_init(hex: "#252629")
  102. textField!.textField.font = NSFont.SFProTextSemiboldFont(20.0)
  103. textField?.maxLen = 1
  104. textField?.model.isCanNull = true
  105. textField?.textField.tag = i + 10
  106. textField!.textField.alignment = .center
  107. textField?.superview?.border(NSColor.km_init(hex: "#DFE1E5"), 1, 2)
  108. textField?.textField.onFocus = { [weak self] in
  109. self?.cancelAllTextFieldFouce()
  110. textField?.superview?.border(NSColor.km_init(hex: "#1770F4"), 1, 2)
  111. }
  112. textField?.textDidEndEditing = { [weak self] string in
  113. textField?.superview?.border(NSColor.km_init(hex: "#DFE1E5"), 1, 2)
  114. }
  115. textField?.textDidChange = { [weak self] string in
  116. //自动验证验证码
  117. self?.autoVerificationCode()
  118. var isNext = true
  119. if string == "" {
  120. isNext = false
  121. }
  122. for item in textFieldArray {
  123. let t = textField?.textField
  124. var tag = t!.tag + 1
  125. if !isNext {
  126. tag = t!.tag - 1
  127. }
  128. tag = max(10, tag)
  129. if item?.textField.tag == tag {
  130. item?.textField.becomeFirstResponder()
  131. item?.superview?.border(NSColor.km_init(hex: "#1770F4"), 1, 2)
  132. break
  133. }
  134. }
  135. }
  136. textField?.textDeleteAction = { [weak self] string in
  137. if string == "" {
  138. for item in textFieldArray {
  139. let t = textField?.textField
  140. let tag = max(10, t!.tag - 1)
  141. if item?.textField.tag == tag {
  142. item?.textField.becomeFirstResponder()
  143. item?.superview?.border(NSColor.km_init(hex: "#1770F4"), 1, 2)
  144. break
  145. }
  146. }
  147. }
  148. }
  149. }
  150. }
  151. self.cancellationButtonVC = KMDesignButton(withType: .Text)
  152. self.cancellationButton.addSubview(self.cancellationButtonVC.view)
  153. self.cancellationButtonVC?.view.frame = self.cancellationButton.bounds
  154. self.cancellationButtonVC.target = self
  155. self.cancellationButtonVC.action = #selector(doneButtonAction)
  156. self.cancellationButtonVC.button(type: .Cta, size: .m)
  157. self.cancellationButtonVC.button.keyEquivalent = KMKeyEquivalent.enter
  158. self.cancelButtonVC = KMDesignButton(withType: .Text)
  159. self.cancelButton.addSubview(self.cancelButtonVC.view)
  160. self.cancelButtonVC?.view.frame = self.cancelButton.bounds
  161. self.cancelButtonVC.target = self
  162. self.cancelButtonVC.action = #selector(cancelButtonAction)
  163. self.cancelButtonVC.button(type: .Sec, size: .m)
  164. self.cancelButtonVC.button.keyEquivalent = KMKeyEquivalent.enter
  165. self.closeBox.moveCallback = { [weak self] (mouseEntered, mouseBox) in
  166. if mouseEntered {
  167. self?.closeButton.image = NSImage(named: "control_btn_icon_close_hov")
  168. } else {
  169. self?.closeButton.image = NSImage(named: "control_btn_icon_close")
  170. }
  171. }
  172. }
  173. func reloadData() {
  174. if inputType == .accountInfo {
  175. self.closeButton.isHidden = false
  176. self.cancelButtonVC.button(type: .Sec, size: .m)
  177. self.cancelButtonVC.updateUI()
  178. } else {
  179. self.closeButton.isHidden = true
  180. self.cancelButtonVC.button(type: .Text, size: .m)
  181. self.cancelButtonVC.updateUI()
  182. }
  183. let textFieldArray = [self.code1TextField,self.code2TextField,self.code3TextField,self.code4TextField,self.code5TextField,self.code6TextField]
  184. for item in textFieldArray {
  185. item?.isEnabled = !self.isNetworking
  186. }
  187. if self.verificationCode.count == 6 &&
  188. // self.time != -1 &&
  189. self.codeIsTrue {
  190. self.cancellationButtonVC.enabled = true
  191. } else {
  192. self.cancellationButtonVC.enabled = false
  193. self.showAlert(result: Result(code: 0))
  194. }
  195. }
  196. func updateLanguage() {
  197. if self.verifyCodeType == .unknown {
  198. self.titleLabel.stringValue = ""
  199. } else if self.verifyCodeType == .register {
  200. self.titleLabel.stringValue = NSLocalizedString("Sign Up", comment: "")
  201. } else if self.verifyCodeType == .logOff {
  202. self.titleLabel.stringValue = NSLocalizedString("Cancel Account", comment: "")
  203. } else if self.verifyCodeType == .resetPassword {
  204. self.titleLabel.stringValue = NSLocalizedString("Reset Password", comment: "")
  205. }
  206. self.describeLabel.stringValue = NSLocalizedString("Enter the verification code", comment: "")
  207. if inputType == .unknown {
  208. self.cancelButtonVC.stringValue = ""
  209. } else if inputType == .accountInfo {
  210. self.cancelButtonVC.stringValue = NSLocalizedString("Cancel", comment: "")
  211. } else {
  212. self.cancelButtonVC.stringValue = NSLocalizedString("Back to previous step", comment: "")
  213. }
  214. if self.verifyCodeType == .unknown {
  215. self.cancellationButtonVC.stringValue = ""
  216. } else if self.verifyCodeType == .register {
  217. self.cancellationButtonVC.stringValue = NSLocalizedString("Sign Up", comment: "")
  218. } else if self.verifyCodeType == .logOff {
  219. self.cancellationButtonVC.stringValue = NSLocalizedString("Cancellation", comment: "")
  220. } else if self.verifyCodeType == .resetPassword {
  221. self.cancellationButtonVC.stringValue = NSLocalizedString("Next Step", comment: "")
  222. }
  223. let tempTime: String = String(self.time)
  224. var timeString = "(" + tempTime + NSLocalizedString("s", comment: "") + ")"
  225. if tempTime == "60" {
  226. timeString = NSLocalizedString("Click to resend", comment: "")
  227. } else if tempTime == "-1" {
  228. timeString = " "
  229. }
  230. //singin
  231. let string = NSLocalizedString("Didn't receive the verification code?", comment: "") + " " + timeString
  232. let attributedString = NSMutableAttributedString.init(string: string)
  233. let paragraphStyle = NSMutableParagraphStyle()
  234. paragraphStyle.alignment = .left;
  235. attributedString.addAttributes([NSAttributedString.Key.font : NSFont.SFProTextRegularFont(12.0),
  236. NSAttributedString.Key.foregroundColor : NSColor.km_init(hex: "#252629"),
  237. NSAttributedString.Key.paragraphStyle : paragraphStyle],
  238. range: NSRange(location: 0, length: string.count))
  239. let range = string.range(of: NSLocalizedString(timeString, comment: ""))
  240. attributedString.setAttributes([NSAttributedString.Key.font : NSFont.SFProTextRegularFont(12.0),
  241. NSAttributedString.Key.foregroundColor : NSColor.km_init(hex: "#1770F4"),
  242. NSAttributedString.Key.underlineColor : NSColor.clear,
  243. NSAttributedString.Key.link : "timer://"],
  244. range: string.nsRange(from: range!)!)
  245. self.timerTextView.textStorage?.setAttributedString(attributedString)
  246. }
  247. func cleanVerificationCode() {
  248. let textFieldArray = [self.code1TextField,self.code2TextField,self.code3TextField,self.code4TextField,self.code5TextField,self.code6TextField]
  249. for item in textFieldArray {
  250. item?.stringValue = ""
  251. item?.textField.resignFirstResponder()
  252. if item != self.code1TextField {
  253. item?.superview?.border(NSColor.km_init(hex: "#DFE1E5"), 1, 2)
  254. }
  255. }
  256. self.model.verifyCode = ""
  257. }
  258. //验证码请求完成
  259. func updateNetworkingState(complete: Bool, codeIsTure: Bool) {
  260. self.isNetworking = !complete
  261. self.codeIsTrue = codeIsTure
  262. self.reloadData()
  263. if !codeIsTure {
  264. self.code6TextField.textField.becomeFirstResponder()
  265. }
  266. }
  267. //自动验证验证码
  268. func autoVerificationCode() {
  269. let textFieldArray = [self.code1TextField,self.code2TextField,self.code3TextField,self.code4TextField,self.code5TextField,self.code6TextField]
  270. for item in textFieldArray {
  271. item?.textField.resignFirstResponder()
  272. }
  273. if self.verificationCode.count == 6 {
  274. if self.verificationCodeAction != nil {
  275. self.isNetworking = true
  276. self.model.verifyCode = self.verificationCode
  277. self.verificationCodeAction!(self, self.model, self.verificationCode)
  278. }
  279. }
  280. }
  281. }
  282. protocol KMVerificationCodeViewTimer {}
  283. extension KMVerificationCodeView: KMVerificationCodeViewTimer {
  284. func cleanTimer() {
  285. self.time = startTime
  286. self.updateLanguage()
  287. }
  288. func resetTimer() {
  289. self.time = startTime
  290. self.beginTimer()
  291. self.updateTimerData(timer: self.timer!)
  292. }
  293. func beginTimer() {
  294. if self.timer != nil {
  295. self.endTimer()
  296. }
  297. if self.time == -1 {
  298. self.time = startTime
  299. }
  300. self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimerData), userInfo: nil, repeats: true)
  301. }
  302. func endTimer() {
  303. self.timer?.invalidate()
  304. self.timer = nil
  305. self.updateLanguage()
  306. }
  307. @objc func updateTimerData(timer: Timer) {
  308. self.time -= 1
  309. if self.time == 0 {
  310. self.endTimer()
  311. self.time = startTime
  312. }
  313. self.updateLanguage()
  314. }
  315. }
  316. protocol KMVerificationCodeViewAction {}
  317. extension KMVerificationCodeView: KMVerificationCodeViewAction {
  318. func sendVerificationCode() {
  319. guard let callBack = reSendAction else { return }
  320. self.timerTextView.isSelectable = false
  321. callBack(self, self.timerTextView)
  322. }
  323. @IBAction func cancelButtonAction(_ sender: NSButton) {
  324. self.endTimer()
  325. guard let callBack = cancelAction else { return }
  326. callBack(self)
  327. }
  328. @IBAction func doneButtonAction(_ sender: NSButton) {
  329. guard let callBack = doneAction else { return }
  330. self.model.verifyCode = self.verificationCode
  331. self.changeDoneButtonState(enable: false)
  332. callBack(self, self.model, sender)
  333. }
  334. @IBAction func closeButtonAction(_ sender: Any) {
  335. guard let callBack = closeAction else { return }
  336. self.endTimer()
  337. callBack(self)
  338. }
  339. func showAlert(result: Result?) {
  340. if result != nil {
  341. self.alertView.result = result!
  342. self.alertHeightConstraint.constant = self.alertView.fetchAlertHeight()
  343. if result?.code != 0 && result?.code != 200 {
  344. self.textFieldAlert()
  345. }
  346. }
  347. }
  348. func changeDoneButtonState(enable: Bool) {
  349. self.cancellationButtonVC.enabled = enable
  350. }
  351. func cancelAllTextFieldFouce() {
  352. let textFieldArray = [self.code1TextField,self.code2TextField,self.code3TextField,self.code4TextField,self.code5TextField,self.code6TextField]
  353. for item in textFieldArray {
  354. item?.superview?.border(NSColor.km_init(hex: "#DFE1E5"), 1, 2)
  355. }
  356. }
  357. func textFieldAlert() {
  358. let textFieldArray = [self.code1TextField,self.code2TextField,self.code3TextField,self.code4TextField,self.code5TextField,self.code6TextField]
  359. for item in textFieldArray {
  360. item?.superview?.border(NSColor.red, 1, 2)
  361. }
  362. }
  363. }
  364. extension KMVerificationCodeView: NSTextViewDelegate {
  365. func textView(_ textView: NSTextView, clickedOnLink link: Any, at charIndex: Int) -> Bool {
  366. if link as! String == "timer://" && self.time == 60 {
  367. guard let callBack = reSendAction else { return true }
  368. textView.isSelectable = false
  369. callBack(self, textView)
  370. }
  371. return true
  372. }
  373. }
  374. extension KMVerificationCodeView: NSTextFieldDelegate {
  375. func controlTextDidEndEditing(_ obj: Notification) {
  376. KMPrint("controlTextDidEndEditing")
  377. let textField = obj.object as? NSTextField
  378. for view in self.textFieldContentView.subviews {
  379. let t = view.subviews.first as? FocusAwareTextField
  380. if t == textField {
  381. view.border(NSColor.km_init(hex: "#DFE1E5"), 1, 4)
  382. }
  383. }
  384. }
  385. }
  386. extension KMVerificationCodeView {
  387. override var acceptsFirstResponder: Bool {
  388. return true
  389. }
  390. override func performKeyEquivalent(with event: NSEvent) -> Bool {
  391. if event.type == .keyDown, event.modifierFlags.contains(.command), event.characters == "v" {
  392. if let pasteboardString = NSPasteboard.general.string(forType: .string) {
  393. KMPrint(pasteboardString)
  394. if let num = Int(pasteboardString) {
  395. let textFieldArray = [self.code1TextField,self.code2TextField,self.code3TextField,self.code4TextField,self.code5TextField,self.code6TextField]
  396. for index in 0...textFieldArray.count - 1 {
  397. if index < pasteboardString.count {
  398. let textField = textFieldArray[index]
  399. let fifthChar = pasteboardString[pasteboardString.index(pasteboardString.startIndex, offsetBy: index)]
  400. textField?.textField.stringValue = String(fifthChar)
  401. textField?.stringValue = String(fifthChar)
  402. textField?.textField.becomeFirstResponder()
  403. }
  404. }
  405. if pasteboardString.count == 6 {
  406. self.autoVerificationCode()
  407. }
  408. }
  409. // 处理剪贴板中的字符串
  410. return true
  411. }
  412. }
  413. return super.performKeyEquivalent(with: event)
  414. }
  415. }