KMNBetaFeedbackWindowController.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. //
  2. // KMNBetaFeedbackWindowController.swift
  3. // PDF Reader Pro Beta
  4. //
  5. // Created by kdanmobile on 2025/3/1.
  6. //
  7. import Cocoa
  8. import KMComponentLibrary
  9. class KMNBetaFeedbackWindowController: KMNBaseWindowController {
  10. @IBOutlet var titleLabel: NSTextField!
  11. @IBOutlet var stateImageView: NSImageView!
  12. @IBOutlet var stateLabel: NSTextField!
  13. @IBOutlet var stateImageTopConstraint:NSLayoutConstraint!
  14. @IBOutlet var feedbackLabel: NSTextField!
  15. @IBOutlet var feedbackTextView: NSTextView!
  16. @IBOutlet var feedbackBox: NSBox!
  17. @IBOutlet var addFileLabel: NSTextField!
  18. @IBOutlet var addFileToolTip: ComponentToolTipsHelp!
  19. @IBOutlet var listTabelView: NSTableView!
  20. @IBOutlet var emailLabel: NSTextField!
  21. @IBOutlet var webLabel: NSTextField!
  22. @IBOutlet var addFileButton: ComponentButton!
  23. @IBOutlet var cancelButton: ComponentButton!
  24. @IBOutlet var applyButton: ComponentButton!
  25. @IBOutlet var addFileWidthButton:NSLayoutConstraint!
  26. @IBOutlet var cancelWidthButton:NSLayoutConstraint!
  27. @IBOutlet var applyWidthButton:NSLayoutConstraint!
  28. @objc var submitActionCallback: ((_ isSuccess: Bool)->Void)?
  29. private var _fileFormats: [String] = ["jpg", "jpeg", "png", "gif", "bmp", "tiff", "tif", "psd", "svg", "pdf", "mp4", "mov", "avi", "mkv", "wmv", "flv", "mpg", "3gp", "doc", "docx", "xls" ,"xlsx", "ppt", "pptx", "txt", "rtf", "nfo"]
  30. @objc static let shared: KMNBetaFeedbackWindowController = {
  31. let windowC = KMNBetaFeedbackWindowController(windowNibName: "KMNBetaFeedbackWindowController")
  32. return windowC
  33. }()
  34. private var _filePaths: [String] = []
  35. var datas: [KMUserFbListModel] = [] {
  36. didSet {
  37. listTabelView.reloadData()
  38. }
  39. }
  40. var isEndTime: Bool! {
  41. get {
  42. //需拿远端的控制的时间
  43. return true
  44. }
  45. }
  46. override func windowDidLoad() {
  47. super.windowDidLoad()
  48. }
  49. override func close() {
  50. if(KMNBetaFeedbackManager.defalutManager.isSupportOpenApp == false) {
  51. NSApplication.shared.terminate(nil)
  52. }
  53. }
  54. override func initContentView() {
  55. titleLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-l-medium")
  56. stateLabel.font = ComponentLibrary.shared.getFontFromKey ("mac/body-xs-regular")
  57. feedbackLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-m-medium")
  58. feedbackTextView.font = ComponentLibrary.shared.getFontFromKey("mac/body-m-medium")
  59. addFileLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-m-medium")
  60. applyButton.properties = ComponentButtonProperty(type: .primary,
  61. size: .s,
  62. state: .normal,
  63. buttonText: KMLocalizedString("Submit"))
  64. applyButton.setTarget(self, action: #selector(submitButtonClicked(_ :)))
  65. applyButton.keyEquivalent = KMKeyEquivalent.enter
  66. cancelButton.properties = ComponentButtonProperty(type: .default_tertiary,
  67. size: .s,
  68. state: .normal,
  69. buttonText: KMLocalizedString("Cancel"))
  70. cancelButton.setTarget(self, action: #selector(cancelButtonClicked(_ :)))
  71. cancelButton.keyEquivalent = KMKeyEquivalent.esc.string()
  72. addFileButton.properties = ComponentButtonProperty(type: .secondary,
  73. size: .s,
  74. state: .normal,
  75. buttonText: KMLocalizedString("Add File"),
  76. icon:NSImage(named: "KMNImageNameBetaAddFile"))
  77. addFileButton.setTarget(self, action: #selector(addFileButtonClicked(_ :)))
  78. if(KMMemberInfo.shared.isMemberAllFunction) {
  79. if KMLocalizedString("USD", tableName: "MemberCenterLocalizable", comment: "") == "CN" {
  80. stateImageView.image = NSImage(named: "KMNImageNameBetaFeedbackAIZh")
  81. } else {
  82. stateImageView.image = NSImage(named: "KMNImageNameBetaFeedbackAIEn")
  83. }
  84. } else {
  85. if KMLocalizedString("USD", tableName: "MemberCenterLocalizable", comment: "") == "CN" {
  86. stateImageView.image = NSImage(named: "KMNImageNameBetaFeedbackZh")
  87. } else {
  88. stateImageView.image = NSImage(named: "KMNImageNameBetaFeedbackEn")
  89. }
  90. }
  91. if(isEndTime) {
  92. stateImageTopConstraint.constant = 40
  93. stateLabel.isHidden = false
  94. } else {
  95. stateImageTopConstraint.constant = 15
  96. stateLabel.isHidden = true
  97. }
  98. listTabelView.dataSource = self
  99. listTabelView.delegate = self
  100. listTabelView.rowHeight = 40
  101. listTabelView.register(.init(nibNamed: "KMNBetaFeedbackTableCellView", bundle: nil), forIdentifier: .init("KMNBetaFeedbackTableCellView"))
  102. listTabelView.enclosingScrollView?.wantsLayer = true
  103. listTabelView.enclosingScrollView?.borderType = .noBorder
  104. listTabelView.enclosingScrollView?.drawsBackground = false
  105. listTabelView.enclosingScrollView?.backgroundColor = .clear
  106. listTabelView.enclosingScrollView?.layer?.backgroundColor = .clear
  107. listTabelView.backgroundColor = .clear
  108. listTabelView.registerForDraggedTypes([.fileURL])
  109. listTabelView.intercellSpacing = NSSize(width: 8, height: 8) // 设置水平和垂直间距
  110. }
  111. override func updateUIThemeColor() {
  112. window?.contentView?.wantsLayer = true
  113. window?.contentView?.layer?.backgroundColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/popup").cgColor
  114. feedbackBox.borderColor = ComponentLibrary.shared.getComponentColorFromKey("comp-field/colorBorder-nor");
  115. titleLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorError/base")
  116. stateLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey ("colorText/2")
  117. feedbackLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("comp-form/colorText-label")
  118. feedbackTextView.textColor = ComponentLibrary.shared.getComponentColorFromKey("comp-form/colorText-label")
  119. addFileLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("comp-form/colorText-label")
  120. cancelButton.reloadData()
  121. applyButton.reloadData()
  122. addFileButton.reloadData()
  123. updateLabel()
  124. }
  125. override func updateUILanguage() {
  126. titleLabel.stringValue = KMLocalizedString("Beta's Usage Rights Has Expired")
  127. stateLabel.stringValue = "* " + KMLocalizedString("Your attachments are strictly confidential and will only be used for the current operation.")
  128. feedbackLabel.stringValue = KMLocalizedString("Feedback (required)")
  129. feedbackTextView.km_placeholderString = KMLocalizedString("Please describe your suggestions in detail so that we can better improve our product.")
  130. addFileLabel.stringValue = KMLocalizedString("Add Attachment Document")
  131. var toolTips = "-" + KMLocalizedString("Limit file size to 20M, upload up to 10 files")
  132. toolTips = toolTips + "\n" + "-" + "No limitations on file formats ( PDF, Excel, Word, Powerpoint, TXT, HTML, MP4, Images and so on)"
  133. addFileToolTip.toolTip = toolTips
  134. cancelButton.properties.buttonText = KMLocalizedString("Cancel")
  135. applyButton.properties.buttonText = KMLocalizedString("Submit")
  136. addFileButton.properties.buttonText = KMLocalizedString("Add File")
  137. cancelButton.reloadData()
  138. applyButton.reloadData()
  139. addFileButton.reloadData()
  140. cancelWidthButton.constant = cancelButton.properties.propertyInfo.viewWidth
  141. applyWidthButton.constant = applyButton.properties.propertyInfo.viewWidth
  142. addFileWidthButton.constant = addFileButton.properties.propertyInfo.viewWidth
  143. updateLabel()
  144. }
  145. func updateLabel() {
  146. let text = KMLocalizedString("Reward will be distributed to your PDF Reader Pro account: %@")
  147. let email = KMMemberInfo.shared.userEmail
  148. let fullString = String(format: text, email)
  149. let attrStr = NSMutableAttributedString(string: fullString)
  150. attrStr.addAttribute(.font, value: ComponentLibrary.shared.getFontFromKey("mac/body-xs-regular"), range: NSRange(location: 0, length: fullString.count))
  151. attrStr.addAttribute(.foregroundColor, value: ComponentLibrary.shared.getComponentColorFromKey("colorText/2"), range: NSRange(location: 0, length: fullString.count))
  152. attrStr.addAttribute(.foregroundColor, value: ComponentLibrary.shared.getComponentColorFromKey("colorPrimary/textLight"), range: (fullString as NSString).range(of: email))
  153. let paragraphStyle = NSMutableParagraphStyle()
  154. paragraphStyle.alignment = .center
  155. attrStr.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: fullString.count))
  156. // 应用富文本
  157. emailLabel.attributedStringValue = attrStr
  158. let linkText = KMLocalizedString("Please visit our Official Website to Sign in to check your reward.")
  159. let linkAttrStr = NSMutableAttributedString(string: linkText)
  160. let link = KMLocalizedString("Official Website")
  161. // 设置超链接文本属性
  162. let url = URL(string: "https://www.pdfreaderpro.com")!
  163. linkAttrStr.addAttribute(.link, value: url, range: (linkText as NSString).range(of: link))
  164. // 设置字体和颜色
  165. linkAttrStr.addAttribute(.font, value: ComponentLibrary.shared.getFontFromKey("mac/body-xs-regular"), range: NSRange(location: 0, length: linkText.count))
  166. linkAttrStr.addAttribute(.foregroundColor, value: ComponentLibrary.shared.getComponentColorFromKey("colorText/2"), range: NSRange(location: 0, length: linkText.count))
  167. linkAttrStr.addAttribute(.foregroundColor, value: ComponentLibrary.shared.getComponentColorFromKey("colorPrimary/textLight"), range: (linkText as NSString).range(of: link))
  168. linkAttrStr.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: (linkText as NSString).range(of: link))
  169. linkAttrStr.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: linkText.count))
  170. webLabel.isEditable = false
  171. webLabel.isSelectable = true
  172. webLabel.allowsEditingTextAttributes = true
  173. // 应用富文本到 NSTextField
  174. webLabel.attributedStringValue = linkAttrStr
  175. }
  176. private func _updateListData() {
  177. var datas: [KMUserFbListModel] = []
  178. let maxSize: Double = 20 * 1024 * 1024
  179. var fileSize: Double = 0
  180. var filePaths: [String] = []
  181. var showFileSizeLimit = false
  182. var showFileCountLimit = false
  183. for (i, fileP) in _filePaths.enumerated() {
  184. let model = KMUserFbListModel()
  185. model.filePath = fileP
  186. let url = URL(fileURLWithPath: fileP)
  187. model.fileName = url.lastPathComponent
  188. let attri = try?FileManager.default.attributesOfItem(atPath: fileP)
  189. model.fileSize = attri?[FileAttributeKey.size] as? Double ?? 0
  190. fileSize = model.fileSize
  191. if fileSize >= maxSize {
  192. showFileSizeLimit = true
  193. continue
  194. }
  195. if i >= 10 {
  196. showFileCountLimit = true
  197. break
  198. }
  199. model.fileSizeString = self.fileSizeString(model.fileSize)
  200. datas.append(model)
  201. filePaths.append(fileP)
  202. }
  203. _filePaths = filePaths
  204. if showFileCountLimit {
  205. _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Add failed: upload up to 10 files"),
  206. type: .warning,
  207. fromView: self.window?.contentView ?? NSView(),
  208. point:CGPointMake(CGRectGetMidX(self.window?.contentView?.bounds ?? CGRect.zero), CGRectGetMidY(self.window?.contentView?.bounds ?? CGRect.zero)))
  209. } else if showFileSizeLimit {
  210. _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Add failed: attachment size cannot exceed 20M"),
  211. type: .warning,
  212. fromView: self.window?.contentView ?? NSView(),
  213. point:CGPointMake(CGRectGetMidX(self.window?.contentView?.bounds ?? CGRect.zero), CGRectGetMidY(self.window?.contentView?.bounds ?? CGRect.zero)))
  214. }
  215. if datas.count >= 10 {
  216. addFileButton.properties.state = .pressed
  217. } else {
  218. addFileButton.properties.state = .normal
  219. }
  220. self.datas = datas
  221. }
  222. func fileSizeString(_ fSize: Double) -> String {
  223. let fileSize = fSize / 1024
  224. let size = fileSize >= 1024 ? (fileSize < 1048576 ? fileSize/1024 : fileSize/1048576.0) : fileSize
  225. let unit = fileSize >= 1024 ? (fileSize < 1048576 ? "M" : "G") : "K"
  226. return String(format: "%0.1f %@", size, unit)
  227. }
  228. private func _fileFormatIsContains(_ exn: String) -> Bool {
  229. let _exn = exn.lowercased()
  230. return _fileFormats.contains(_exn)
  231. }
  232. //MARK: - Action
  233. @objc func cancelButtonClicked(_ sender: NSView) {
  234. close()
  235. }
  236. @objc func submitButtonClicked(_ sender: NSView) {
  237. close()
  238. submitActionCallback?(true)
  239. }
  240. @objc func addFileButtonClicked(_ sender: NSView) {
  241. let panel = NSOpenPanel()
  242. panel.allowedFileTypes = _fileFormats
  243. panel.allowsMultipleSelection = true
  244. panel.beginSheetModal(for: self.window!) { [self] resp in
  245. if resp == .cancel {
  246. return
  247. }
  248. for url in panel.urls {
  249. self._filePaths.append(url.path)
  250. }
  251. _updateListData()
  252. }
  253. }
  254. }
  255. extension KMNBetaFeedbackWindowController: NSTableViewDelegate, NSTableViewDataSource {
  256. func numberOfRows(in tableView: NSTableView) -> Int {
  257. return datas.count
  258. }
  259. func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
  260. var cell = tableView.makeView(withIdentifier: .init("KMNBetaFeedbackTableCellView"), owner: self)
  261. if cell == nil {
  262. cell = KMNBetaFeedbackTableCellView.createFromNib()
  263. }
  264. let cellView = cell as? KMNBetaFeedbackTableCellView
  265. let model = datas[row]
  266. cellView?.fileNameLabel.stringValue = model.fileName ?? ""
  267. cellView?.fileSizeLabel.stringValue = model.fileSizeString ?? ""
  268. cellView?.deleteItemClick = { [weak self] idx in
  269. if let cnt = self?._filePaths.count, row < cnt {
  270. self?._filePaths.remove(at: row)
  271. self?._updateListData()
  272. }
  273. }
  274. return cell
  275. }
  276. func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
  277. let datas = _filePaths.count
  278. if datas >= 10 {
  279. return NSDragOperation(rawValue: 0)
  280. }
  281. return .generic
  282. }
  283. func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
  284. let pborad = info.draggingPasteboard
  285. guard let _ = pborad.availableType(from: [.fileURL]) else {
  286. return false
  287. }
  288. guard let items = pborad.pasteboardItems else {
  289. return false
  290. }
  291. for item in items {
  292. let string = item.propertyList(forType: .fileURL) as? String ?? ""
  293. if let path = URL(string: string)?.path {
  294. let contains = _fileFormatIsContains(URL(string: string)!.pathExtension)
  295. if contains {
  296. _filePaths.append(path)
  297. }
  298. }
  299. }
  300. self._updateListData()
  301. return true
  302. }
  303. }
  304. extension KMNBetaFeedbackWindowController: NSTextViewDelegate {
  305. func textViewDidChangeSelection(_ notification: Notification) {
  306. if self.feedbackTextView.isEqual(to: notification.object) {
  307. self.feedbackTextView.placeholderLabel.isHidden = self.feedbackTextView.string.count > 0
  308. }
  309. }
  310. }