123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- //
- // KMUserFeekbackWindowController.swift
- // PDF Reader Pro Edition
- //
- // Created by tangchao on 2024/7/11.
- //
- import Cocoa
- @objcMembers class KMUserFeekbackWindowController: NSWindowController {
- static let shared = KMUserFeekbackWindowController(windowNibName: "KMUserFeekbackWindowController")
- @IBOutlet weak var emailBox: NSBox!
- @IBOutlet weak var typeBox: NSBox!
- @IBOutlet weak var despBox: NSBox!
- @IBOutlet weak var listHeaderBox: NSBox!
- @IBOutlet weak var listBox: NSBox!
- @IBOutlet weak var comfirmButton: NSButton!
- @IBOutlet weak var cancelButton: NSButton!
-
- private var emailItemView_: KMUserFbEmailItemView?
- private var typeItemView_: KMUserFbTypeItemView?
- private var despItemView_: KMUserFbDespItemView?
- private var listHeaderItemView_: KMUserFbListHeaderItemView?
- private var listItemView_: KMUserListItemView?
-
- typealias KMRequestServerComplete = (_ wrapper: ResultWrapper) -> Void
-
- private var filePaths_: [String] = []
-
- private var logPath_: String?
-
- private var popover_: NSPopover?
-
- var typeString: String = ""
-
- 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"]
- private var fileFormatsString = "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"
-
- deinit {
- Swift.debugPrint("KMUserFeekbackWindowController deinit.")
-
- DistributedNotificationCenter.default().removeObserver(self)
- }
-
- override func windowDidLoad() {
- super.windowDidLoad()
- self._initDefaultValue()
- self._initSubViews()
-
- self.typeItemView_?.comBoBox.selectItem(at: 0)
-
- self.updateViewColor()
- DistributedNotificationCenter.default().addObserver(self, selector: #selector(themeChanged), name: NSApplication.interfaceThemeChangedNotification, object: nil)
- }
-
- private func _initDefaultValue() {
- if let btn = self.window?.standardWindowButton(.miniaturizeButton) {
- btn.isHidden = true
- }
- if let btn = self.window?.standardWindowButton(.zoomButton) {
- btn.isHidden = true
- }
- self.window?.title = NSLocalizedString("Feedback for PDF Reader Pro", comment: "")
-
- self.emailBox.borderWidth = 0
- self.typeBox.borderWidth = 0
- self.despBox.borderWidth = 0
- self.listHeaderBox.borderWidth = 0
- self.listBox.borderWidth = 0
-
- let email = VerificationManager.default()?.email ?? ""
- self.emailItemView_?.textfiled.stringValue = email
-
- self.emailItemView_?.textfiled.nextResponder = self.despItemView_?.textView
-
- self.cancelButton.title = NSLocalizedString("Cancel", comment: "")
- self.comfirmButton.title = NSLocalizedString("Submit", comment: "")
-
- self.cancelButton.target = self
- self.cancelButton.action = #selector(_cancelButtonAction)
- self.comfirmButton.target = self
- self.comfirmButton.action = #selector(_comfirmButtonAction)
- }
-
- private func _initSubViews() {
- self.emailItemView_ = KMUserFbEmailItemView.createFromNib()
- self.emailBox.contentView = self.emailItemView_
-
- self.typeItemView_ = KMUserFbTypeItemView.createFromNib()
- self.typeBox.contentView = self.typeItemView_
-
- self.despItemView_ = KMUserFbDespItemView.createFromNib()
- self.despBox.contentView = self.despItemView_
-
- self.listHeaderItemView_ = KMUserFbListHeaderItemView.createFromNib()
- self.listHeaderBox.contentView = self.listHeaderItemView_
-
- self.listItemView_ = KMUserListItemView.createFromNib()
- self.listBox.contentView = self.listItemView_
-
- self.listItemView_?.hiddenTip()
-
- self.listHeaderItemView_?.helpHoverCallback = { [weak self] action in
- if action.rawValue == 0 { // enter
- self?._showHelpPopover((self?.listHeaderItemView_?.helpButton)!)
- } else if action.rawValue == 2 { // exit
- self?._hiddenHelpPopover()
- }
- }
- self.listHeaderItemView_?.itemClick = { [weak self] idx in
- if idx == 1 { // 获取日志文件
-
- } else if idx == 3 { // 新增文件
- let panel = NSOpenPanel()
- // panel.message = self?.fileFormatsString
- panel.allowedFileTypes = self?.fileFormats_
- panel.allowsMultipleSelection = true
- panel.beginSheetModal(for: self!.window!) { resp in
- if resp == .cancel {
- return
- }
-
- for url in panel.urls {
- self?.filePaths_.append(url.path)
- }
-
- self?._updateListData()
- }
- }
- }
-
- self.listItemView_?.itemClick = { [weak self] idx, param in
- if let cnt = self?.filePaths_.count, idx < cnt {
- self?.filePaths_.remove(at: idx)
-
- self?._updateListData()
- }
- }
-
- self.listItemView_?.validateDropCallback = { [weak self] info, row, dropOperation in
- let datas = self?.filePaths_.count ?? 0
- if datas >= 10 {
- return NSDragOperation(rawValue: 0)
- }
- return .generic
- }
-
- self.listItemView_?.acceptDropCallback = { [weak self] info, row, dropOperation in
- let pborad = info.draggingPasteboard
- guard let _ = pborad.availableType(from: [.fileURL]) else {
- return false
- }
- guard let items = pborad.pasteboardItems else {
- return false
- }
- for item in items {
- let string = item.propertyList(forType: .fileURL) as? String ?? ""
- if let path = URL(string: string)?.path {
- let contains = self?._fileFormatIsContains(URL(string: string)!.pathExtension) ?? false
- if contains {
- self?.filePaths_.append(path)
- }
- }
- }
- self?._updateListData()
- return true
- }
- }
-
- override func showWindow(_ sender: Any?) {
- super.showWindow(sender)
-
- self.typeItemView_?.comBoBox.stringValue = self.typeString
- }
-
- func updateViewColor() {
- if KMAppearance.isDarkMode() {
- self.window?.backgroundColor = NSColor(red: 40/255.0, green: 40/255.0, blue: 40/255.0, alpha: 1)
- self.emailItemView_?.box.fillColor = NSColor(red: 110/255.0, green: 109/255.0, blue: 112/255.0, alpha: 1)
- self.typeItemView_?.comBoBox.backgroundColor = NSColor(red: 110/255.0, green: 109/255.0, blue: 112/255.0, alpha: 1)
- self.despItemView_?.box.fillColor = NSColor(red: 110/255.0, green: 109/255.0, blue: 112/255.0, alpha: 1)
- self.listItemView_?.tipBox.fillColor = NSColor(red: 231/255.0, green: 56/255.0, blue: 91/255.0, alpha: 1)
- } else {
- self.window?.backgroundColor = .white
- self.emailItemView_?.box.fillColor = .white
- self.typeItemView_?.comBoBox.backgroundColor = .white
- self.despItemView_?.box.fillColor = .white
- self.listItemView_?.tipBox.fillColor = NSColor.km_init(hex: "#FEE4EC")
- }
- }
-
- // MARK: - Noti Methods
-
- @objc func themeChanged(_ notification: Notification) {
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
- self.updateViewColor()
- }
- }
-
- // MARK: - Private Methods
-
- @objc private func _cancelButtonAction() {
- self.window?.orderOut(nil)
- }
-
- @objc private func _comfirmButtonAction() {
- if let data = self.listHeaderItemView_?.checkButton.state, data == .on {
- let fileaccess = AppSandboxFileAccess()
- // /Users/kdanmobile/Library/Logs/DiagnosticReports
- let url = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first
- let coms = url?.pathComponents ?? []
- var filePath: String = ""
- for (i, com) in coms.enumerated() {
- if i > 2 {
- break
- } else {
- filePath.append("/\(com)")
- }
- }
- let cmd = filePath.appending("/Library/Logs/DiagnosticReports/")
- // /Library/Logs/DiagnosticReports/
- // fileaccess?.accessFilePath(cmd, persistPermission: true, with: {
- //
- // })
-
- let logString = self._getLog()
- if logString.isEmpty == false {
- let bundleIdentifier = Bundle.main.bundleIdentifier ?? "PDF Reader Pro"
- if let url = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first?.appendingPathComponent(bundleIdentifier) {
- if FileManager.default.fileExists(atPath: url.path) == false {
- try?FileManager.default.createDirectory(at: url, withIntermediateDirectories: false)
- }
-
- let fileUrl = url.appendingPathComponent("Log.txt")
-
- try?logString.write(to: fileUrl, atomically: true, encoding: .utf8)
-
- self.logPath_ = fileUrl.path
- }
- }
- } else {
- self.logPath_ = nil
- }
-
- self.window?.makeFirstResponder(nil)
- let state = self._isConnectionAvailable()
- if !state {
- self._showHud(msg: NSLocalizedString("Please make sure your internet connection is available.", comment: ""))
- return
- }
-
- guard let emailString = self.emailItemView_?.string(), emailString.isEmpty == false else {
- // 邮箱为空
- self.emailItemView_?.showTip(label: NSLocalizedString("Please enter your email", comment: ""))
- return
- }
-
- if KMValidateEmail(email: emailString) == false {
- self.emailItemView_?.showTip(label: NSLocalizedString("Please enter valid email", comment: ""))
- return
- }
-
- guard let despString = self.despItemView_?.string(), despString.isEmpty == false else {
- // 描述信息为空
- self.despItemView_?.showTip()
- return
- }
-
- self.window?.contentView?.beginLoading()
- self.comfirmButton.isEnabled = false
- var filePaths: [String] = self.filePaths_
- if let data = self.listHeaderItemView_?.checkButton.state, data == .on {
- if let filePath = self.logPath_, filePath.isEmpty == false {
- filePaths.append(filePath)
- }
- }
-
- self._feekbackAction(filePaths: filePaths) { [weak self] wrapper in
- self?.window?.contentView?.endLoading()
- self?.comfirmButton.isEnabled = true
- if wrapper.success {
- self?._showHud(msg: NSLocalizedString("Thank you for your feedback! Customer service will feedback to your email within 1 working day", comment: ""))
-
- DispatchQueue.main.asyncAfter(deadline: .now() + 1.8) {
- self?.despItemView_?.textView.string = ""
- self?.despItemView_?.textView.placeholderLabel.isHidden = false;
- self?.filePaths_.removeAll()
- self?._updateListData()
-
- if let data = self?.logPath_, data.isEmpty == false {
- if FileManager.default.fileExists(atPath: data) {
- try?FileManager.default.removeItem(atPath: data)
- }
- }
-
- self?.logPath_ = nil
- self?.window?.orderOut(nil)
- }
- } else {
- self?._showHud(msg: wrapper.content)
- }
- }
- }
-
- private func _showHelpPopover(_ sender: NSButton) {
- if self.popover_ != nil {
- return
- }
- let vc = KMUserFbHelpPopController()
- vc.formatSting = self.fileFormatsString
- let popover = NSPopover()
- popover.contentViewController = vc
- popover.animates = true
- popover.behavior = .semitransient
- popover.delegate = self
- popover.setValue(true, forKey: "shouldHideAnchor")
- self.popover_ = popover
-
- vc.isDrak = KMAppearance.isDarkMode()
-
- popover.show(relativeTo: sender.bounds, of: sender, preferredEdge: .maxX)
- }
-
- private func _hiddenHelpPopover() {
- self.popover_?.close()
- }
-
- private func _updateListData() {
- self.listItemView_?.hiddenTip()
-
- var datas: [KMUserFbListModel] = []
- let maxSize: Double = 20 * 1024 * 1024
- var fileSize: Double = 0
- var filePaths: [String] = []
- var showFileSizeLimit = false
- var showFileCountLimit = false
- for (i, fileP) in self.filePaths_.enumerated() {
- let model = KMUserFbListModel()
- model.filePath = fileP
- let url = URL(fileURLWithPath: fileP)
- model.fileName = url.lastPathComponent
- let attri = try?FileManager.default.attributesOfItem(atPath: fileP)
- model.fileSize = attri?[FileAttributeKey.size] as? Double ?? 0
-
- // fileSize += model.fileSize
- fileSize = model.fileSize
- if fileSize >= maxSize {
- showFileSizeLimit = true
- continue
- }
- if i >= 10 {
- showFileCountLimit = true
- break
- }
-
- model.fileSizeString = self.fileSizeString(model.fileSize) ?? ""
- datas.append(model)
-
- filePaths.append(fileP)
- }
- self.filePaths_ = filePaths
-
- if showFileCountLimit {
- self.listItemView_?.showTip(tip: NSLocalizedString("Add failed: upload up to 10 files", comment: ""))
- } else if showFileSizeLimit {
- self.listItemView_?.showTip(tip: NSLocalizedString("Add failed: attachment size cannot exceed 20M", comment: ""))
- }
-
- if datas.count >= 10 {
- self.listHeaderItemView_?.addButton.isEnabled = false
- } else {
- self.listHeaderItemView_?.addButton.isEnabled = true
- }
-
- self.listItemView_?.datas = datas
- }
-
- private func _showHud(msg: String) {
- if let data = self.window?.contentView {
- // _ = CustomAlertView(message: msg, from: data, with: .black)
- CustomAlertView.alertView(message: msg, fromView: data, withStyle: .black)
- }
- }
-
- private func _getLog() -> String {
- let url = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first
- let coms = url?.pathComponents ?? []
- var filePath: String = ""
- for (i, com) in coms.enumerated() {
- if i > 2 {
- break
- } else {
- filePath.append("/\(com)")
- }
- }
- // ls /Library/Logs/DiagnosticReports/PDF*
- let cmd = "ls \(filePath)/Library/Logs/DiagnosticReports/PDF*"
- let filestrings = _runShellCommand(cmd)
- let files = filestrings.components(separatedBy: "\n")
- var results: [String] = []
- for string in files {
- if string.contains("PDF Reader Pro") {
- var tempS = string
- // tempS.replace(" ", with: "\\ ")
- // tempS.replacingCharacters(in: " ", with: "\\ ")
- // let ss = tempS.replacing(" ", with: "\\ ")
- let ss = tempS.replacingOccurrences(of: " ", with: "\\ ")
- results.append(ss)
- }
- }
-
- if results.isEmpty {
- let cmd = "ls \(filePath)/Library/Logs/DiagnosticReports/Retired/PDF*"
- let filestrings = _runShellCommand(cmd)
- let files = filestrings.components(separatedBy: "\n")
- for string in files {
- if string.contains("PDF Reader Pro") {
- var tempS = string
- // tempS.replace(" ", with: "\\ ")
- // tempS.replacingCharacters(in: " ", with: "\\ ")
- // let ss = tempS.replacing(" ", with: "\\ ")
- let ss = tempS.replacingOccurrences(of: " ", with: "\\ ")
- results.append(ss)
- }
- }
- }
-
- if let file = results.last {
- let cmd = "cat \(file)"
- // cat /Library/Logs/DiagnosticReports/PDF*
- let logs = _runShellCommand(cmd)
- return logs
- }
- return ""
- }
-
- private func _runShellCommand(_ command: String) -> String {
- let task = Process()
- let pipe = Pipe()
-
- task.standardOutput = pipe
- task.standardError = pipe
- task.arguments = ["-c", command]
- task.launchPath = "/bin/bash"
-
- do {
- try task.run()
- } catch {
- return "Error running command: \(error)"
- }
-
- let data = pipe.fileHandleForReading.readDataToEndOfFile()
- let output = String(data: data, encoding: .utf8) ?? ""
- return output
- }
-
- func test() {
- var convertString = "TARGET_BUNDLE_IDENTIFIER=\"com.brother.pdfreaderprofree.mac\"\n"
- convertString = convertString.appendingFormat("SANDBOX_ROOT=$(osascript -e 'tell application \"System Events\" to get the path of the home folder of (system info)')\n")
- convertString = convertString.appendingFormat("CONTAINER_LOG_PATH=$(osascript <<EOF\n")
- convertString.append("tell application \"System Events\"\n")
- // convertString.append("set isRun to running\n")
- convertString.append("set container_path to POSIX path of (the home folder as text) & \"Library:Containers:\" & \"$TARGET_BUNDLE_IDENTIFIER\"\n")
- convertString.append("set log_path to container_path & \":Logs:\" & \"Console.app-History\"\n")
- convertString.append("return log_path\n")
- convertString.append("end tell\n")
- convertString.append("EOF)\n")
-
- convertString.append("echo \"日志文件路径: $CONTAINER_LOG_PATH\"\n")
- var dic: NSDictionary?
- let docScript = NSAppleScript(source: convertString)
- docScript?.executeAndReturnError(&dic)
- }
-
- func fileSizeString(_ fSize: Double) -> String {
- let fileSize = fSize / 1024
- let size = fileSize >= 1024 ? (fileSize < 1048576 ? fileSize/1024 : fileSize/1048576.0) : fileSize
- let unit = fileSize >= 1024 ? (fileSize < 1048576 ? "M" : "G") : "K"
- return String(format: "%0.1f %@", size, unit)
- }
-
- private func _feekbackAction(filePaths: [String], complete: @escaping KMRequestServerComplete) {
- let urlString = kVerificationServer + "/api/feedback/feedback"
- var fileDatas: [Data] = []
- for filePath in filePaths {
- if let fileData = FileManager.default.contents(atPath: filePath) {
- fileDatas.append(fileData)
- }
- }
- let (major, minor, bugFix) = KMTools.getSystemVersion()
- let versionInfoString = "\(KMTools.getRawSystemInfo()) - \(major).\(minor).\(bugFix)"
-
- let appVersion = KMTools.getAppVersion()
- let appName = KMTools.getAppNameForSupportEmail()
-
- let typeString = (self.typeItemView_?.string() ?? "") + ";" + appName + " - " + appVersion + ";" + versionInfoString
- let uuid = GetHardwareUUID()
- let params: [String: Any] = ["email": self.emailItemView_?.string() ?? "",
- "title": typeString,
- "content": self.despItemView_?.string() ?? "",
- "unique_sn" : uuid!,
- "files[]" : fileDatas]
- KMAIRequestServer.requestServer.uploadFile(urlString: urlString, params: params) { formData in
- for i in 0 ..< fileDatas.count {
- let path = filePaths[i]
- let fileURL = URL(fileURLWithPath: path)
- try? formData.appendPart(withFileURL: fileURL, name: "files[]", fileName: fileURL.lastPathComponent, mimeType: "application/octet-stream")
- }
- } requestSerializer: { requestSerializer in
- requestSerializer.setValue("Apifox/1.0.0 (https://www.apifox.cn)", forHTTPHeaderField: "User-Agent")
- } completion: { task, responseObject, error in
- if responseObject != nil {
- let data: NSDictionary = responseObject as? NSDictionary ?? [:]
- // var code: String = responseObject!["code"] as? String ?? "06005"
- let code = data["code"] as? Int ?? 06005
- if code == 06005 {
- // let tempCode: Int = responseObject!["code"] as? Int ?? 0
- // if tempCode == 501 {
- // code = "501"
- // }
- }
-
- let message: String = data["message"] as? String ?? "unknown error"
- let error = NSError(domain: message, code: code)
- if code == 200 {
- let wrapper = ResultWrapper(success: true, resultData: data)
- // wrapper.content = data["fileKey"] as! String
- complete(wrapper)
- } else {
- let wrapper = ResultWrapper(success: false, resultData: data)
- if code == 501 {
- wrapper.content = "501"
- } else {
- wrapper.content = message
- }
- complete(wrapper)
- }
- } else {
- complete(ResultWrapper(success: false, content: "unknown error"))
- }
- }
- }
-
- private func _isConnectionAvailable() -> Bool {
- if Reachability.forInternetConnection().currentReachabilityStatus().rawValue == 0 {
- return false
- }
- return true
- }
-
- private func _fileFormatIsContains(_ exn: String) -> Bool {
- let _exn = exn.lowercased()
- return self.fileFormats_.contains(_exn)
- }
-
- }
- extension KMUserFeekbackWindowController: NSPopoverDelegate {
- func popoverWillClose(_ notification: Notification) {
- if let data = self.popover_?.isEqual(to: notification.object), data {
- self.popover_ = nil
- }
- }
- }
|