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