From 0446e93ba9d3c6793073b28f4a7b2dcb5d2b9296 Mon Sep 17 00:00:00 2001 From: Svend Date: Mon, 15 Jul 2019 09:25:41 +0800 Subject: [PATCH 1/2] :sparkles: new feature custom host --- uPic.xcodeproj/project.pbxproj | 4 + uPic/AppDelegate.swift | 5 + uPic/Models/Custom/CustomHostConfig.swift | 10 +- uPic/Models/Custom/CustomUploader.swift | 20 +-- .../Base.lproj/Preferences.storyboard | 131 ++++++++++++++++-- .../ConfigView/ConfigView.swift | 4 +- .../CustomConfigSheetController.swift | 45 ++++++ .../ConfigView/Views/CustomConfigView.swift | 90 ++++++------ .../HostPreferencesViewController.swift | 5 + .../PreferencesViewController.swift | 10 ++ .../PreferencesWindowController.swift | 8 +- uPic/Views/StatusMenuController.swift | 3 +- uPic/en.lproj/Localizable.strings | 6 +- uPic/zh-Hans.lproj/Localizable.strings | 4 + 14 files changed, 263 insertions(+), 82 deletions(-) create mode 100644 uPic/PreferencesWindow/ConfigView/CustomConfigSheetController.swift diff --git a/uPic.xcodeproj/project.pbxproj b/uPic.xcodeproj/project.pbxproj index d6066fa..71d664a 100644 --- a/uPic.xcodeproj/project.pbxproj +++ b/uPic.xcodeproj/project.pbxproj @@ -64,6 +64,7 @@ 167DDBED22C76F6600B03357 /* GithubHostConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167DDBEC22C76F6600B03357 /* GithubHostConfig.swift */; }; 167DDBEF22C7722200B03357 /* GithubUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167DDBEE22C7722200B03357 /* GithubUtil.swift */; }; 167DDBF122C7784900B03357 /* GithubConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167DDBF022C7784900B03357 /* GithubConfigView.swift */; }; + 1683573C22DB0D0800985B10 /* CustomConfigSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1683573B22DB0D0800985B10 /* CustomConfigSheetController.swift */; }; 168A831122B606C000344E77 /* UpYunUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16995A2022B5FC7B00B1F923 /* UpYunUploader.swift */; }; 1690E7E422BF111500FC81F8 /* QiniuConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1690E7E322BF111500FC81F8 /* QiniuConfigView.swift */; }; 1690E7E722BF174300FC81F8 /* QiniuHostConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1690E7E622BF12D200FC81F8 /* QiniuHostConfig.swift */; }; @@ -165,6 +166,7 @@ 167DDBEC22C76F6600B03357 /* GithubHostConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubHostConfig.swift; sourceTree = ""; }; 167DDBEE22C7722200B03357 /* GithubUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubUtil.swift; sourceTree = ""; }; 167DDBF022C7784900B03357 /* GithubConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubConfigView.swift; sourceTree = ""; }; + 1683573B22DB0D0800985B10 /* CustomConfigSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomConfigSheetController.swift; sourceTree = ""; }; 1690E7E022BE32B100FC81F8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Preferences.strings; sourceTree = ""; }; 1690E7E222BE32D500FC81F8 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Preferences.strings"; sourceTree = ""; }; 1690E7E322BF111500FC81F8 /* QiniuConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QiniuConfigView.swift; sourceTree = ""; }; @@ -358,6 +360,7 @@ children = ( 1646B80F22C7C485009271DF /* Views */, 1646B80D22C7C082009271DF /* ConfigView.swift */, + 1683573B22DB0D0800985B10 /* CustomConfigSheetController.swift */, 166B4A5422B9CFD7001288ED /* ConfigSheetController.swift */, ); path = ConfigView; @@ -737,6 +740,7 @@ 1690E7E722BF174300FC81F8 /* QiniuHostConfig.swift in Sources */, 1675516222ABF80300D3EB6F /* NSDragingInfoExt.swift in Sources */, 1662AC7322C0BBF7003AC924 /* AliyunRegion.swift in Sources */, + 1683573C22DB0D0800985B10 /* CustomConfigSheetController.swift in Sources */, 169F074422AF7A3D008E8525 /* StatusMenuController.swift in Sources */, 167DDBEB22C76F5000B03357 /* GithubUploader.swift in Sources */, 169F073922AF4549008E8525 /* GeneralPreferencesViewController.swift in Sources */, diff --git a/uPic/AppDelegate.swift b/uPic/AppDelegate.swift index da77197..a578ab0 100644 --- a/uPic/AppDelegate.swift +++ b/uPic/AppDelegate.swift @@ -160,7 +160,12 @@ extension AppDelegate { } else { NotificationExt.sendUploadErrorNotification(body: NSLocalizedString("file-format-is-not-supported", comment: "文件格式不支持")) } + + } else if (NSPasteboard.general.types?.first == NSPasteboard.PasteboardType.png) { + let imgData = NSPasteboard.general.data(forType: NSPasteboard.PasteboardType.png) + self.uploadFiles([imgData!]) } + } @objc func screenshotAndUpload() { diff --git a/uPic/Models/Custom/CustomHostConfig.swift b/uPic/Models/Custom/CustomHostConfig.swift index 981e542..7f57628 100644 --- a/uPic/Models/Custom/CustomHostConfig.swift +++ b/uPic/Models/Custom/CustomHostConfig.swift @@ -16,8 +16,8 @@ class CustomHostConfig: HostConfig { dynamic var field: String! dynamic var extensions: String? dynamic var headers: String? + dynamic var resultPath: String? dynamic var domain: String? - dynamic var folder: String? dynamic var saveKey: String? = HostSaveKey.filename.rawValue override func displayName(key: String) -> String { @@ -32,10 +32,10 @@ class CustomHostConfig: HostConfig { return NSLocalizedString("host.field.extensions", comment: "extensions") case "headers": return NSLocalizedString("host.field.headers", comment: "headers") + case "resultPath": + return NSLocalizedString("host.field.resultPath", comment: "resultPath") case "domain": return NSLocalizedString("host.field.domain", comment: "domain") - case "folder": - return NSLocalizedString("host.field.folder", comment: "folder") case "saveKey": return NSLocalizedString("host.field.saveKey", comment: "fileName") default: @@ -50,8 +50,8 @@ class CustomHostConfig: HostConfig { dict["field"] = self.field dict["extensions"] = self.extensions dict["headers"] = self.headers + dict["resultPath"] = self.resultPath dict["domain"] = self.domain - dict["folder"] = self.folder dict["saveKey"] = self.saveKey return JSON(dict).rawString()! @@ -69,8 +69,8 @@ class CustomHostConfig: HostConfig { config.field = json["field"].stringValue config.extensions = json["extensions"].stringValue config.headers = json["headers"].stringValue + config.resultPath = json["resultPath"].stringValue config.domain = json["domain"].stringValue - config.folder = json["folder"].stringValue config.saveKey = json["saveKey"].stringValue return config } diff --git a/uPic/Models/Custom/CustomUploader.swift b/uPic/Models/Custom/CustomUploader.swift index 0f316e9..a06f171 100644 --- a/uPic/Models/Custom/CustomUploader.swift +++ b/uPic/Models/Custom/CustomUploader.swift @@ -50,11 +50,6 @@ class CustomUploader: BaseUploader { mimeType = Util.getMimeType(pathExtension: "png") } - var key = fileName - if (config.folder != nil && config.folder!.count > 0) { - key = "\(config.folder!)/\(key)" - } - var headers = HTTPHeaders() headers.add(HTTPHeader.contentType("application/x-www-form-urlencoded;charset=utf-8")) if let headersStr = config.headers { @@ -69,12 +64,6 @@ class CustomUploader: BaseUploader { case "{filename}": value = fileName break - case "{path}": - value = key - break - case "{folder}": - value = config.folder ?? "" - break default: break } @@ -83,7 +72,6 @@ class CustomUploader: BaseUploader { } func multipartFormDataGen(multipartFormData: MultipartFormData) { - multipartFormData.append(key.data(using: .utf8)!, withName: "key") if let extensionsStr = config.extensions { let extensionsArr = extensionsStr.split(separator: Character("&")) @@ -98,12 +86,6 @@ class CustomUploader: BaseUploader { case "{filename}": value = fileName break - case "{path}": - value = key - break - case "{folder}": - value = config.folder ?? "" - break default: break } @@ -124,7 +106,7 @@ class CustomUploader: BaseUploader { }.response(completionHandler: { response -> Void in switch response.result { case .success(_): - super.completed(url: "\(domain)/\(key)") + super.completed(url: "\(domain)/\(fileName)") case .failure(let error): super.faild(errorMsg: error.localizedDescription) } diff --git a/uPic/PreferencesWindow/Base.lproj/Preferences.storyboard b/uPic/PreferencesWindow/Base.lproj/Preferences.storyboard index c42fdfe..4176c6d 100644 --- a/uPic/PreferencesWindow/Base.lproj/Preferences.storyboard +++ b/uPic/PreferencesWindow/Base.lproj/Preferences.storyboard @@ -501,6 +501,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -687,16 +808,6 @@ - - - diff --git a/uPic/PreferencesWindow/ConfigView/ConfigView.swift b/uPic/PreferencesWindow/ConfigView/ConfigView.swift index c3ee998..c9d546a 100644 --- a/uPic/PreferencesWindow/ConfigView/ConfigView.swift +++ b/uPic/PreferencesWindow/ConfigView/ConfigView.swift @@ -123,7 +123,7 @@ class ConfigView: NSView { @objc func openConfigSheet(_ sender: NSButton) { if let configSheetController = configSheetController { - let userInfo: [String: Any] = ["domain": self.data?.value(forKey: "domain") ?? "", "folder": self.data?.value(forKey: "folder") ?? "", "saveKey": self.data?.value(forKey: "saveKey") ?? HostSaveKey.dateFilename.rawValue] + let userInfo: [String: Any] = ["domain": self.data?.value(forKey: "domain") ?? "", "saveKey": self.data?.value(forKey: "saveKey") ?? HostSaveKey.dateFilename.rawValue] self.window?.contentViewController?.presentAsSheet(configSheetController) configSheetController.setData(userInfo: userInfo as [String: AnyObject]) self.addObserver() @@ -138,11 +138,9 @@ class ConfigView: NSView { } let domain = userInfo["domain"] as? String ?? "" - let folder = userInfo["folder"] as? String ?? "" let saveKey = userInfo["saveKey"] as? String ?? HostSaveKey.dateFilename.rawValue self.data?.setValue(domain, forKey: "domain") - self.data?.setValue(folder, forKey: "folder") self.data?.setValue(saveKey, forKey: "saveKey") domainField?.stringValue = domain diff --git a/uPic/PreferencesWindow/ConfigView/CustomConfigSheetController.swift b/uPic/PreferencesWindow/ConfigView/CustomConfigSheetController.swift new file mode 100644 index 0000000..e1729aa --- /dev/null +++ b/uPic/PreferencesWindow/ConfigView/CustomConfigSheetController.swift @@ -0,0 +1,45 @@ +// +// CustomConfigSheetController.swift +// uPic +// +// Created by Svend Jin on 2019/7/14. +// Copyright © 2019 Svend Jin. All rights reserved. +// + +import Cocoa + +class CustomConfigSheetController: NSViewController { + + @IBOutlet weak var cancelButton: NSButton! + @IBOutlet weak var okButton: NSButton! + + @IBOutlet weak var addHeaderButton: NSButton! + @IBOutlet weak var addBodyButton: NSButton! + @IBOutlet weak var scrollView: NSScrollView! + + var headers:Dictionary?; + var bodys:Dictionary?; + + override func viewDidLoad() { + super.viewDidLoad() + // Do view setup here. + + okButton.highlight(true) + } + + + @IBAction func addHeaderBtnClicked(_ sender: Any) { + + } + + @IBAction func addBodyBtnClicked(_ sender: Any) { + } + + @IBAction func okBtnClicked(_ sender: Any) { + self.dismiss(sender) + } + + func refreshScroolView() { + + } +} diff --git a/uPic/PreferencesWindow/ConfigView/Views/CustomConfigView.swift b/uPic/PreferencesWindow/ConfigView/Views/CustomConfigView.swift index d04d4c3..d283e64 100644 --- a/uPic/PreferencesWindow/ConfigView/Views/CustomConfigView.swift +++ b/uPic/PreferencesWindow/ConfigView/Views/CustomConfigView.swift @@ -9,6 +9,16 @@ import Cocoa class CustomConfigView: ConfigView { + + var postConfigSheetController: CustomConfigSheetController?; + + override func draw(_ dirtyRect: NSRect) { + super.draw(dirtyRect) + + postConfigSheetController = (self.window?.contentViewController?.storyboard!.instantiateController(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CustomConfigSheetController").rawValue) + as! CustomConfigSheetController) + + } override func createView() { @@ -16,7 +26,7 @@ class CustomConfigView: ConfigView { return } - let paddingTop = 30, paddingLeft = 6, gapTop = 10, gapLeft = 5, labelWidth = 75, labelHeight = 20, textAreaHeight = 50, + let paddingTop = 50, paddingLeft = 6, gapTop = 10, gapLeft = 5, labelWidth = 75, labelHeight = 20, viewWidth = Int(self.frame.width), viewHeight = Int(self.frame.height), textFieldX = labelWidth + paddingLeft + gapLeft, textFieldWidth = viewWidth - paddingLeft - textFieldX @@ -70,61 +80,53 @@ class CustomConfigView: ConfigView { // MARK: field y = y - gapTop - labelHeight + let otherFieldsBtnWith = 100 + let fieldLabel = NSTextField(labelWithString: "\(data.displayName(key: "field")):") fieldLabel.frame = NSRect(x: paddingLeft, y: y, width: labelWidth, height: labelHeight) fieldLabel.alignment = .right fieldLabel.lineBreakMode = .byClipping - let fieldField = NSTextField(frame: NSRect(x: textFieldX, y: y, width: textFieldWidth, height: labelHeight)) + let fieldField = NSTextField(frame: NSRect(x: textFieldX, y: y, width: textFieldWidth - otherFieldsBtnWith, height: labelHeight)) fieldField.identifier = NSUserInterfaceItemIdentifier(rawValue: "field") fieldField.usesSingleLineMode = true fieldField.lineBreakMode = .byTruncatingTail fieldField.delegate = data fieldField.stringValue = data.field ?? "" + + + let otherFieldsBtn = NSButton(title: NSLocalizedString("host.field.other-fields", comment: "其他字段"), target: self, action: #selector(openCustomConfigSheet(_:))) + otherFieldsBtn.frame = NSRect(x: textFieldX + Int(fieldField.frame.width) + gapLeft, y: y, width: otherFieldsBtnWith, height: labelHeight) + otherFieldsBtn.imagePosition = .noImage + self.addSubview(fieldLabel) self.addSubview(fieldField) + self.addSubview(otherFieldsBtn) nextKeyViews.append(fieldField) + nextKeyViews.append(otherFieldsBtn) - // MARK: Extensions + + // MARK: resultPath y = y - gapTop - labelHeight - let extensionsLabel = NSTextField(labelWithString: "\(data.displayName(key: "extensions")):") - extensionsLabel.frame = NSRect(x: paddingLeft, y: y, width: labelWidth, height: labelHeight) - extensionsLabel.alignment = .right - extensionsLabel.lineBreakMode = .byClipping - - let extensionsField = NSTextField(frame: NSRect(x: textFieldX, y: y + labelHeight - textAreaHeight, width: textFieldWidth, height: textAreaHeight)) - extensionsField.usesSingleLineMode = false - extensionsField.lineBreakMode = .byWordWrapping - extensionsField.cell?.wraps = true - extensionsField.identifier = NSUserInterfaceItemIdentifier(rawValue: "extensions") - extensionsField.delegate = data - extensionsField.stringValue = data.extensions ?? "" - extensionsField.placeholderString = "eg: key=value&key2=value2" - self.addSubview(extensionsLabel) - self.addSubview(extensionsField) - nextKeyViews.append(extensionsField) - - // MARK: Headers - y = y - gapTop - textAreaHeight - let headersLabel = NSTextField(labelWithString: "\(data.displayName(key: "headers")):") - headersLabel.frame = NSRect(x: paddingLeft, y: y, width: labelWidth, height: labelHeight) - headersLabel.alignment = .right - headersLabel.lineBreakMode = .byClipping - - let headersField = NSTextField(frame: NSRect(x: textFieldX, y: y + labelHeight - textAreaHeight, width: textFieldWidth, height: textAreaHeight)) - headersField.usesSingleLineMode = false - headersField.lineBreakMode = .byWordWrapping - headersField.cell?.wraps = true - headersField.identifier = NSUserInterfaceItemIdentifier(rawValue: "headers") - headersField.delegate = data - headersField.stringValue = data.headers ?? "" - headersField.placeholderString = "eg: key=value&key2=value2" - self.addSubview(headersLabel) - self.addSubview(headersField) - nextKeyViews.append(headersField) - + let resultLabel = NSTextField(labelWithString: "URL 路径:") + resultLabel.frame = NSRect(x: paddingLeft, y: y, width: labelWidth, height: labelHeight) + resultLabel.alignment = .right + resultLabel.lineBreakMode = .byClipping + + let resultField = NSTextField(frame: NSRect(x: textFieldX, y: y, width: textFieldWidth, height: labelHeight)) + resultField.identifier = NSUserInterfaceItemIdentifier(rawValue: "resultPath") + resultField.usesSingleLineMode = true + resultField.lineBreakMode = .byTruncatingTail + resultField.delegate = data + resultField.stringValue = data.resultPath ?? "" + resultField.placeholderString = NSLocalizedString("host.placeholder.resultPath", comment: "") + resultField.toolTip = NSLocalizedString("host.placeholder.resultPath", comment: "") + self.addSubview(resultLabel) + self.addSubview(resultField) + nextKeyViews.append(resultField) + // MARK: domain - y = y - gapTop - textAreaHeight + y = y - gapTop - labelHeight let settingsBtnWith = 40 let domainLabel = NSTextField(labelWithString: "\(data.displayName(key: "domain")):") @@ -138,7 +140,8 @@ class CustomConfigView: ConfigView { domainField.lineBreakMode = .byTruncatingTail domainField.delegate = data domainField.stringValue = data.domain ?? "" - domainField.placeholderString = NSLocalizedString("host.placeholder.domain", comment: "") + domainField.placeholderString = NSLocalizedString("host.placeholder.custom-domain", comment: "") + domainField.toolTip = NSLocalizedString("host.placeholder.custom-domain", comment: "") self.domainField = domainField let settingsBtn = NSButton(title: "", image: NSImage(named: NSImage.advancedName)!, target: self, action: #selector(openConfigSheet(_:))) @@ -151,6 +154,7 @@ class CustomConfigView: ConfigView { nextKeyViews.append(domainField) nextKeyViews.append(settingsBtn) + // MARK: help y = y - gapTop - labelHeight let helpBtnSize = 21 @@ -168,4 +172,8 @@ class CustomConfigView: ConfigView { self.data?.setValue(identifier, forKey: "method") } } + + @objc func openCustomConfigSheet(_ sender: NSButton) { + self.window?.contentViewController?.presentAsSheet(postConfigSheetController!) + } } diff --git a/uPic/PreferencesWindow/HostPreferencesViewController.swift b/uPic/PreferencesWindow/HostPreferencesViewController.swift index 7a2062c..cb4910d 100644 --- a/uPic/PreferencesWindow/HostPreferencesViewController.swift +++ b/uPic/PreferencesWindow/HostPreferencesViewController.swift @@ -357,4 +357,9 @@ extension HostPreferencesViewController: NSWindowDelegate { return true } } + + func windowWillClose(_ notification: Notification) { + // 关闭偏好设置时在去掉 Dock 栏显示应用图标 + NSApp.setActivationPolicy(.accessory) + } } diff --git a/uPic/PreferencesWindow/PreferencesViewController.swift b/uPic/PreferencesWindow/PreferencesViewController.swift index 4c870dc..8094825 100644 --- a/uPic/PreferencesWindow/PreferencesViewController.swift +++ b/uPic/PreferencesWindow/PreferencesViewController.swift @@ -22,10 +22,20 @@ class PreferencesViewController: NSViewController { override func viewDidAppear() { super.viewDidAppear() + + // 将应用界面浮到最上层 + NSApp.activate(ignoringOtherApps: true) + view.window?.makeKeyAndOrderFront(self) + + // 打开偏好设置时在 Dock 栏显示应用图标,方便用户再次返回设置界面 + if NSApp.activationPolicy() == .accessory { + NSApp.setActivationPolicy(.regular) + } // MARK: 偏好设置tab切换动画 self.setWindowFrame() } + func setWindowFrame() { if let window = self.view.window { diff --git a/uPic/PreferencesWindow/PreferencesWindowController.swift b/uPic/PreferencesWindow/PreferencesWindowController.swift index 8c43a97..18a62ec 100644 --- a/uPic/PreferencesWindow/PreferencesWindowController.swift +++ b/uPic/PreferencesWindow/PreferencesWindowController.swift @@ -14,5 +14,11 @@ class PreferencesWindowController: NSWindowController { super.windowDidLoad() // Do view setup here. } - + +} +extension PreferencesWindowController: NSWindowDelegate { + func windowWillClose(_ notification: Notification) { + // 关闭偏好设置时在去掉 Dock 栏显示应用图标 + NSApp.setActivationPolicy(.accessory) + } } diff --git a/uPic/Views/StatusMenuController.swift b/uPic/Views/StatusMenuController.swift index 83385eb..f181884 100644 --- a/uPic/Views/StatusMenuController.swift +++ b/uPic/Views/StatusMenuController.swift @@ -34,7 +34,7 @@ class StatusMenuController: NSObject, NSMenuDelegate { selectFileMenuItem.title = NSLocalizedString("status-menu.select-file", comment: "Select file") uploadPasteboardMenuItem.title = NSLocalizedString("status-menu.pasteboard", comment: "Upload with pasteboard") screenshotMenuItem.title = NSLocalizedString("status-menu.screenshot", comment: "Upload with pasteboard") -// hostMenuItem.title = NSLocalizedString("status-menu.host", comment: "Host") + hostMenuItem.title = NSLocalizedString("status-menu.host", comment: "Host") ouputFormatMenuItem.title = NSLocalizedString("status-menu.output", comment: "Choose output format") historyMenuItem.title = NSLocalizedString("status-menu.upload-history", comment: "upload history") preferenceMenuItem.title = NSLocalizedString("status-menu.preference", comment: "Open Preference") @@ -64,7 +64,6 @@ class StatusMenuController: NSObject, NSMenuDelegate { @IBAction func preferenceMenuItemClicked(_ sender: NSMenuItem) { let preferencesWindowController = (NSApplication.shared.delegate as? AppDelegate)?.preferencesWindowController - NSApp.activate(ignoringOtherApps: true) preferencesWindowController?.showWindow(sender) } diff --git a/uPic/en.lproj/Localizable.strings b/uPic/en.lproj/Localizable.strings index efa0922..d2234b7 100644 --- a/uPic/en.lproj/Localizable.strings +++ b/uPic/en.lproj/Localizable.strings @@ -115,7 +115,8 @@ "host.field.saveKey" = "File Name"; "host.field.url" = "API URL"; "host.field.method" = "Method"; -"host.field.field" = "File field"; +"host.field.field" = "File Field"; +"host.field.resultPath" = "URL Path"; "host.field.extensions" = "Fields"; "host.field.headers" = "Headers"; "host.field.owner" = "Owner"; @@ -126,6 +127,7 @@ "host.field.cookieMode" = "Cookie Mode"; "host.field.cookie" = "Cookie"; "host.field.quality" = "Pic Quality"; +"host.field.other-fields" = "Other fields"; /* host field placeholder */ "host.placeholder.operator" = "Operator name"; @@ -133,6 +135,8 @@ "host.placeholder.repo" = "Just the repo name, not the repo URL"; "host.placeholder.domain" = "domain:https://xxx.com"; "host.placeholder.domain-has-default" = "Can be empty, there is a default domain"; +"host.placeholder.resultPath" = "The path to the URL field in Response JSON, eg: data/ URL"; +"host.placeholder.custom-domain" = "(optional),When filled, URL = domain + URL path value"; /*host type*/ diff --git a/uPic/zh-Hans.lproj/Localizable.strings b/uPic/zh-Hans.lproj/Localizable.strings index 62f964a..a87b9fb 100644 --- a/uPic/zh-Hans.lproj/Localizable.strings +++ b/uPic/zh-Hans.lproj/Localizable.strings @@ -113,6 +113,7 @@ "host.field.url" = "API 地址"; "host.field.method" = "请求方式"; "host.field.field" = "文件字段名"; +"host.field.resultPath" = "URL 路径"; "host.field.extensions" = "扩展字段"; "host.field.headers" = "请求头"; "host.field.owner" = "用户名"; @@ -123,6 +124,7 @@ "host.field.cookieMode" = "Cookie 模式"; "host.field.cookie" = "Cookie"; "host.field.quality" = "图片质量"; +"host.field.other-fields" = "其他字段"; /* host field placeholder */ "host.placeholder.operator" = "操作员名称"; @@ -130,6 +132,8 @@ "host.placeholder.repo" = "只需要仓库名称,而不是仓库连接"; "host.placeholder.domain" = "访问域名:https://xxx.com"; "host.placeholder.domain-has-default" = "可为空,有默认域名"; +"host.placeholder.resultPath" = "返回 JSON 中的URL字段的路径,如:data/url"; +"host.placeholder.custom-domain" = "(可选),当填写时,URL = 前缀 + URL 路径值"; /*host type*/ "host.type.-1" = "自定义"; From bcf7f79a23d4cd6a9ed7f7fa06a0a753fb5890ad Mon Sep 17 00:00:00 2001 From: Svend Date: Wed, 17 Jul 2019 15:39:21 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=8E=A8=E9=87=8D=E6=9E=84=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=9B=BE=E5=BA=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🎨 Optimize - 重构自定义图床:更好的配置参数、可配置的结果URL获取规则。具体可看:https://blog.svend.cc/upic/tutorials/custom/ - 在偏好设置打开时,Dock栏会显示uPic应用。主要为了方便在配置图床时在切换了应用后能快速点击Dock栏图标回到偏好设置界面 🐛 Fix Bugs - 修复使用第三方软件截图后,不能通过上传已拷贝文件的bug - 修复其他一些小问题 --- uPic.xcodeproj/project.pbxproj | 8 + uPic/Basic/FlippedView.swift | 17 ++ uPic/Models/Custom/CustomHostConfig.swift | 10 +- uPic/Models/Custom/CustomHostUtil.swift | 61 ++++++ uPic/Models/Custom/CustomUploader.swift | 75 ++++--- uPic/Notifier/PreferencesNotifier.swift | 1 + .../Base.lproj/Preferences.storyboard | 4 +- .../ConfigView/ConfigView.swift | 2 +- .../CustomConfigSheetController.swift | 193 +++++++++++++++++- .../ConfigView/Views/CustomConfigView.swift | 38 +++- uPic/Supporting Files/Info.plist | 4 +- uPic/en.lproj/Localizable.strings | 2 +- uPic/zh-Hans.lproj/Localizable.strings | 2 +- 13 files changed, 361 insertions(+), 56 deletions(-) create mode 100644 uPic/Basic/FlippedView.swift create mode 100644 uPic/Models/Custom/CustomHostUtil.swift diff --git a/uPic.xcodeproj/project.pbxproj b/uPic.xcodeproj/project.pbxproj index 71d664a..e40b43d 100644 --- a/uPic.xcodeproj/project.pbxproj +++ b/uPic.xcodeproj/project.pbxproj @@ -65,6 +65,8 @@ 167DDBEF22C7722200B03357 /* GithubUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167DDBEE22C7722200B03357 /* GithubUtil.swift */; }; 167DDBF122C7784900B03357 /* GithubConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167DDBF022C7784900B03357 /* GithubConfigView.swift */; }; 1683573C22DB0D0800985B10 /* CustomConfigSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1683573B22DB0D0800985B10 /* CustomConfigSheetController.swift */; }; + 1685AA3522DEC943008FBF1D /* FlippedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1685AA3422DEC943008FBF1D /* FlippedView.swift */; }; + 1685AA3722DEEE6C008FBF1D /* CustomHostUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1685AA3622DEEE6C008FBF1D /* CustomHostUtil.swift */; }; 168A831122B606C000344E77 /* UpYunUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16995A2022B5FC7B00B1F923 /* UpYunUploader.swift */; }; 1690E7E422BF111500FC81F8 /* QiniuConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1690E7E322BF111500FC81F8 /* QiniuConfigView.swift */; }; 1690E7E722BF174300FC81F8 /* QiniuHostConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1690E7E622BF12D200FC81F8 /* QiniuHostConfig.swift */; }; @@ -167,6 +169,8 @@ 167DDBEE22C7722200B03357 /* GithubUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubUtil.swift; sourceTree = ""; }; 167DDBF022C7784900B03357 /* GithubConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubConfigView.swift; sourceTree = ""; }; 1683573B22DB0D0800985B10 /* CustomConfigSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomConfigSheetController.swift; sourceTree = ""; }; + 1685AA3422DEC943008FBF1D /* FlippedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlippedView.swift; sourceTree = ""; }; + 1685AA3622DEEE6C008FBF1D /* CustomHostUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomHostUtil.swift; sourceTree = ""; }; 1690E7E022BE32B100FC81F8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Preferences.strings; sourceTree = ""; }; 1690E7E222BE32D500FC81F8 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Preferences.strings"; sourceTree = ""; }; 1690E7E322BF111500FC81F8 /* QiniuConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QiniuConfigView.swift; sourceTree = ""; }; @@ -252,6 +256,7 @@ 161C3BE022C4831B0092114F /* CustomUploader.swift */, 161C3BE222C483560092114F /* CustomHostConfig.swift */, 161C3BE622C49BE90092114F /* RequestMethods.swift */, + 1685AA3622DEEE6C008FBF1D /* CustomHostUtil.swift */, ); path = Custom; sourceTree = ""; @@ -266,6 +271,7 @@ 1647474222B66ACE00F9575D /* Data+Extension.swift */, 165D908522C333740096FF38 /* PasteboardType+Extension.swift */, 165701A022C8A6E600C57EE9 /* Regex.swift */, + 1685AA3422DEC943008FBF1D /* FlippedView.swift */, ); path = Basic; sourceTree = ""; @@ -685,6 +691,7 @@ 165701A322C8AE0A00C57EE9 /* WeiboUtil.swift in Sources */, 16EA205C22AF8FB90006FB9C /* ConfigManager.swift in Sources */, 1675516722ACAA0600D3EB6F /* UPicUpdater.swift in Sources */, + 1685AA3522DEC943008FBF1D /* FlippedView.swift in Sources */, 16068C7822AECB34004D39B7 /* PreferencesWindowController.swift in Sources */, 16995A3422B6008000B1F923 /* UpYunHostConfig.swift in Sources */, 1672762322AFF655007299C3 /* Host.swift in Sources */, @@ -752,6 +759,7 @@ 1660FCBC22C11C7500372950 /* TencentUtil.swift in Sources */, 1660FCBA22C11C2300372950 /* TencentHostConfig.swift in Sources */, 167DDBEF22C7722200B03357 /* GithubUtil.swift in Sources */, + 1685AA3722DEEE6C008FBF1D /* CustomHostUtil.swift in Sources */, 68BBB99780D7F4586458D4F5 /* AliyunUtil.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/uPic/Basic/FlippedView.swift b/uPic/Basic/FlippedView.swift new file mode 100644 index 0000000..79befd0 --- /dev/null +++ b/uPic/Basic/FlippedView.swift @@ -0,0 +1,17 @@ +// +// FlippedView.swift +// uPic +// +// Created by Svend Jin on 2019/7/17. +// Copyright © 2019 Svend Jin. All rights reserved. +// + +import Cocoa + +class FlippedView: NSView { + override var isFlipped:Bool { + get { + return true + } + } +} diff --git a/uPic/Models/Custom/CustomHostConfig.swift b/uPic/Models/Custom/CustomHostConfig.swift index 7f57628..2e3aea6 100644 --- a/uPic/Models/Custom/CustomHostConfig.swift +++ b/uPic/Models/Custom/CustomHostConfig.swift @@ -14,7 +14,7 @@ class CustomHostConfig: HostConfig { dynamic var url: String! dynamic var method: String! = RequestMethods.POST.rawValue dynamic var field: String! - dynamic var extensions: String? + dynamic var bodys: String? dynamic var headers: String? dynamic var resultPath: String? dynamic var domain: String? @@ -28,8 +28,8 @@ class CustomHostConfig: HostConfig { return NSLocalizedString("host.field.method", comment: "method") case "field": return NSLocalizedString("host.field.field", comment: "field") - case "extensions": - return NSLocalizedString("host.field.extensions", comment: "extensions") + case "bodys": + return NSLocalizedString("host.field.bodys", comment: "bodys") case "headers": return NSLocalizedString("host.field.headers", comment: "headers") case "resultPath": @@ -48,7 +48,7 @@ class CustomHostConfig: HostConfig { dict["url"] = self.url dict["method"] = self.method dict["field"] = self.field - dict["extensions"] = self.extensions + dict["bodys"] = self.bodys dict["headers"] = self.headers dict["resultPath"] = self.resultPath dict["domain"] = self.domain @@ -67,7 +67,7 @@ class CustomHostConfig: HostConfig { config.url = json["url"].stringValue config.method = json["method"].stringValue config.field = json["field"].stringValue - config.extensions = json["extensions"].stringValue + config.bodys = json["bodys"].stringValue config.headers = json["headers"].stringValue config.resultPath = json["resultPath"].stringValue config.domain = json["domain"].stringValue diff --git a/uPic/Models/Custom/CustomHostUtil.swift b/uPic/Models/Custom/CustomHostUtil.swift new file mode 100644 index 0000000..e62b0da --- /dev/null +++ b/uPic/Models/Custom/CustomHostUtil.swift @@ -0,0 +1,61 @@ +// +// CustomHostUtil.swift +// uPic +// +// Created by Svend Jin on 2019/7/17. +// Copyright © 2019 Svend Jin. All rights reserved. +// + +import Foundation +import SwiftyJSON + +class CustomHostUtil { + + /** + 解析请求头或者body字符串 + */ + public static func parseHeadersOrBodys(_ str: String) -> [Dictionary] { + var arr: [Dictionary] = [] + if let json = try? JSON(data: str.data(using: String.Encoding.utf8)!) { + if let jsonArr = json.array { + for jsonItem in jsonArr { + arr.append(jsonItem.dictionaryObject as! [String: String]) + } + } + } + return arr + } + + /** + 格式化请求头或者body为字符串 + */ + public static func formatHeadersOrBodys(_ arr: [Dictionary]) -> String { + return JSON(arr).rawString() ?? "" + } + + /** + 获取json结果中的url + */ + public static func parseResultUrl(_ json: JSON, _ resultPath: String) -> String { + var retUrl = "" + var retJson = json + + if !resultPath.isEmpty { + if let pathJSON = try? JSON(data: resultPath.data(using: String.Encoding.utf8)!) { + if let pathArr = pathJSON.arrayObject { + var path: [JSONSubscriptType] = [] + for p in pathArr { + if (p is Int) { + path.append(p as! Int) + } else if (p is String) { + path.append(p as! String) + } + } + retUrl = retJson[path].rawString() ?? "" + } + } + } + + return retUrl + } +} diff --git a/uPic/Models/Custom/CustomUploader.swift b/uPic/Models/Custom/CustomUploader.swift index a06f171..af1fc81 100644 --- a/uPic/Models/Custom/CustomUploader.swift +++ b/uPic/Models/Custom/CustomUploader.swift @@ -8,7 +8,7 @@ import Cocoa import Alamofire -import SwiftyXMLParser +import SwiftyJSON class CustomUploader: BaseUploader { @@ -30,10 +30,10 @@ class CustomUploader: BaseUploader { let field = config.field! let hostSaveKey = HostSaveKey(rawValue: config.saveKey!)! let domain = config.domain! - + let httpMethod = HTTPMethod(rawValue: method) ?? HTTPMethod.post - - + + if url.isEmpty { super.faild(errorMsg: NSLocalizedString("bad-host-config", comment: "bad host config")) return @@ -52,47 +52,37 @@ class CustomUploader: BaseUploader { var headers = HTTPHeaders() headers.add(HTTPHeader.contentType("application/x-www-form-urlencoded;charset=utf-8")) + if let headersStr = config.headers { - let headersArr = headersStr.split(separator: Character("&")) - for headerSr in headersArr { - let headerArr = headerSr.split(separator: Character("=")) - if headerArr.count < 2 { - continue - } - var value = String(headerArr[1]) - switch value { - case "{filename}": - value = fileName - break - default: - break + let headersArr = CustomHostUtil.parseHeadersOrBodys(headersStr) + for header in headersArr { + if let key = header["key"] { + var value = header["value"] ?? "" + if value == "{filename}" { + value = fileName + } + + headers.add(HTTPHeader(name: key, value: value)) } - headers.add(HTTPHeader(name: String(headerArr[0]), value: value)) } } - + + func multipartFormDataGen(multipartFormData: MultipartFormData) { - - if let extensionsStr = config.extensions { - let extensionsArr = extensionsStr.split(separator: Character("&")) - for extensions in extensionsArr { - let extensionArr = extensions.split(separator: Character("=")) - if extensionArr.count < 2 { - continue - } - - var value = String(extensionArr[1]) - switch value { - case "{filename}": - value = fileName - break - default: - break + if let bodysStr = config.bodys { + let bodysArr = CustomHostUtil.parseHeadersOrBodys(bodysStr) + for body in bodysArr { + if let key = body["key"] { + var value = body["value"] ?? "" + if value == "{filename}" { + value = fileName + } + + multipartFormData.append(String(value).data(using: .utf8)!, withName: key) } - multipartFormData.append(String(value).data(using: .utf8)!, withName: String(extensionArr[0])) } } - + if fileUrl != nil { multipartFormData.append(fileUrl!, withName: field, fileName: fileName, mimeType: mimeType) } else { @@ -103,10 +93,15 @@ class CustomUploader: BaseUploader { AF.upload(multipartFormData: multipartFormDataGen, to: url, method: httpMethod, headers: headers).validate().uploadProgress { progress in super.progress(percent: progress.fractionCompleted) - }.response(completionHandler: { response -> Void in + }.responseJSON(completionHandler: { response -> Void in switch response.result { - case .success(_): - super.completed(url: "\(domain)/\(fileName)") + case .success(let value): + let json = JSON(value) + var retUrl = CustomHostUtil.parseResultUrl(json, config.resultPath ?? "") + if !domain.isEmpty { + retUrl = "\(domain)/\(retUrl)" + } + super.completed(url: retUrl) case .failure(let error): super.faild(errorMsg: error.localizedDescription) } diff --git a/uPic/Notifier/PreferencesNotifier.swift b/uPic/Notifier/PreferencesNotifier.swift index 85cf39e..912facd 100644 --- a/uPic/Notifier/PreferencesNotifier.swift +++ b/uPic/Notifier/PreferencesNotifier.swift @@ -13,6 +13,7 @@ public class PreferencesNotifier: Notifier { public enum Notification: String { case openConfigSheet case saveHostSettings + case saveCustomExtensionSettings case hostConfigChanged } diff --git a/uPic/PreferencesWindow/Base.lproj/Preferences.storyboard b/uPic/PreferencesWindow/Base.lproj/Preferences.storyboard index 4176c6d..8c149c1 100644 --- a/uPic/PreferencesWindow/Base.lproj/Preferences.storyboard +++ b/uPic/PreferencesWindow/Base.lproj/Preferences.storyboard @@ -300,7 +300,7 @@ - + @@ -311,7 +311,7 @@ - + diff --git a/uPic/PreferencesWindow/ConfigView/ConfigView.swift b/uPic/PreferencesWindow/ConfigView/ConfigView.swift index c9d546a..724cdae 100644 --- a/uPic/PreferencesWindow/ConfigView/ConfigView.swift +++ b/uPic/PreferencesWindow/ConfigView/ConfigView.swift @@ -118,7 +118,6 @@ class ConfigView: NSView { func removeObserver() { PreferencesNotifier.removeObserver(observer: self, notification: .saveHostSettings) - PreferencesNotifier.addObserver(observer: self, selector: #selector(saveHostSettings), notification: .saveHostSettings) } @objc func openConfigSheet(_ sender: NSButton) { @@ -132,6 +131,7 @@ class ConfigView: NSView { } @objc func saveHostSettings(notification: Notification) { + self.removeObserver() guard let userInfo = notification.userInfo else { print("No userInfo found in notification") return diff --git a/uPic/PreferencesWindow/ConfigView/CustomConfigSheetController.swift b/uPic/PreferencesWindow/ConfigView/CustomConfigSheetController.swift index e1729aa..2a91371 100644 --- a/uPic/PreferencesWindow/ConfigView/CustomConfigSheetController.swift +++ b/uPic/PreferencesWindow/ConfigView/CustomConfigSheetController.swift @@ -7,6 +7,7 @@ // import Cocoa +import SwiftyJSON class CustomConfigSheetController: NSViewController { @@ -17,29 +18,215 @@ class CustomConfigSheetController: NSViewController { @IBOutlet weak var addBodyButton: NSButton! @IBOutlet weak var scrollView: NSScrollView! - var headers:Dictionary?; - var bodys:Dictionary?; + var headers:Array> = []; + var bodys:Array> = []; override func viewDidLoad() { super.viewDidLoad() // Do view setup here. okButton.highlight(true) + scrollView.hasVerticalScroller = true + scrollView.hasHorizontalScroller = true } @IBAction func addHeaderBtnClicked(_ sender: Any) { - + headers.append(["key": "", "value": ""]) + self.refreshScroolView() } @IBAction func addBodyBtnClicked(_ sender: Any) { + bodys.append(["key": "", "value": ""]) + self.refreshScroolView() } @IBAction func okBtnClicked(_ sender: Any) { + let headerStr = CustomHostUtil.formatHeadersOrBodys(self.headers) + let bodyStr = CustomHostUtil.formatHeadersOrBodys(self.bodys) + let userInfo: [String: Any] = ["headers": headerStr, "bodys": bodyStr] + PreferencesNotifier.postNotification(.saveCustomExtensionSettings, object: "CustomConfigSheetController", userInfo: userInfo) + self.dismiss(sender) } func refreshScroolView() { + let paddingLeft = 10, paddingTop = 10, gapLeft = 6, keyWidth = 130, valueWidth = 220, height = 22 + var y = paddingTop + + let contentView = FlippedView() + var nextKeyViews: [NSView] = [] + + if self.headers.count > 0 { + let headersTitle = NSTextField(labelWithString: "Header 数据") + headersTitle.setFrameOrigin(NSPoint(x: paddingLeft, y: y)) + contentView.addSubview(headersTitle) + + for (index, header) in self.headers.enumerated() { + y = y + height + paddingTop + let keyField = NSTextField(frame: NSRect(x: paddingLeft, y: y, width: keyWidth, height: height)) + keyField.identifier = NSUserInterfaceItemIdentifier(rawValue: "header-\(index)-key") + keyField.delegate = self + keyField.stringValue = header["key"] ?? "" + contentView.addSubview(keyField) + nextKeyViews.append(keyField) + + let valueField = NSTextField(frame: NSRect(x: paddingLeft + keyWidth + gapLeft, y: y, width: valueWidth, height: height)) + valueField.identifier = NSUserInterfaceItemIdentifier(rawValue: "header-\(index)-value") + valueField.delegate = self + valueField.stringValue = header["value"] ?? "" + contentView.addSubview(valueField) + nextKeyViews.append(valueField) + + let removeBtn = NSButton(frame: NSRect(x: gapLeft * 2 + paddingLeft + keyWidth + valueWidth, y: y, width: height, height: height)) + removeBtn.identifier = NSUserInterfaceItemIdentifier(rawValue: "header-\(index)-remove_btn") + removeBtn.title = "删除" + removeBtn.image = NSImage(named: NSImage.removeTemplateName) + removeBtn.imagePosition = .imageOnly + removeBtn.target = self + removeBtn.action = #selector(onRemoveItem(_:)) + contentView.addSubview(removeBtn) + } + } + + if self.bodys.count > 0 { + y = y + height + paddingTop + + let bodyTitle = NSTextField(labelWithString: "Body 数据") + bodyTitle.setFrameOrigin(NSPoint(x: paddingLeft, y: y)) + contentView.addSubview(bodyTitle) + + for (index, body) in self.bodys.enumerated() { + y = y + height + paddingTop + let keyField = NSTextField(frame: NSRect(x: paddingLeft, y: y, width: keyWidth, height: height)) + keyField.identifier = NSUserInterfaceItemIdentifier(rawValue: "body-\(index)-key") + keyField.delegate = self + keyField.stringValue = body["key"] ?? "" + contentView.addSubview(keyField) + nextKeyViews.append(keyField) + + let valueField = NSTextField(frame: NSRect(x: paddingLeft + keyWidth + gapLeft, y: y, width: valueWidth, height: height)) + valueField.identifier = NSUserInterfaceItemIdentifier(rawValue: "body-\(index)-value") + valueField.delegate = self + valueField.stringValue = body["value"] ?? "" + contentView.addSubview(valueField) + nextKeyViews.append(valueField) + + let removeBtn = NSButton(frame: NSRect(x: gapLeft * 2 + paddingLeft + keyWidth + valueWidth, y: y, width: height, height: height)) + removeBtn.identifier = NSUserInterfaceItemIdentifier(rawValue: "body-\(index)-remove_btn") + removeBtn.title = "删除" + removeBtn.image = NSImage(named: NSImage.removeTemplateName) + removeBtn.imagePosition = .imageOnly + removeBtn.target = self + removeBtn.action = #selector(onRemoveItem(_:)) + contentView.addSubview(removeBtn) + } + } + + self.setNextKeyViews(nextKeyViews) + + contentView.frame = NSRect(x: 0, y: 0, width: Int(scrollView.frame.width), height: y + height + paddingTop) + scrollView.documentView = contentView + if let documentView = scrollView.documentView { + if documentView.isFlipped { + documentView.scroll(.zero) + } else { + let maxHeight = max(scrollView.bounds.height, documentView.bounds.height) + documentView.scroll(NSPoint(x: 0, y: maxHeight)) + } + } + } + + func setNextKeyViews(_ nextKeyViews: [NSView]) { + if nextKeyViews.count > 1 { + for (index, item) in nextKeyViews.enumerated() { + let currentView = item + if index == nextKeyViews.count - 1 { + break + } + + let nextView = nextKeyViews[index + 1] + currentView.nextKeyView = nextView + + } + } + } + + // 删除某项 + @objc func onRemoveItem(_ sender: NSButton) { + if let identifier = sender.identifier?.rawValue { + let args = identifier.split(separator: Character("-")) + let type = args[0] + let index = Int(args[1])! + + if (index < 0) { + return + } + + if (type == "header") { + self.headers.remove(at: index) + self.refreshScroolView() + } else if (type == "body") { + self.bodys.remove(at: index) + self.refreshScroolView() + } + + } + } + + func setData(headerStr: String, bodyStr: String) { + self.headers.removeAll() + self.bodys.removeAll() + self.headers = CustomHostUtil.parseHeadersOrBodys(headerStr) + self.bodys = CustomHostUtil.parseHeadersOrBodys(bodyStr) + + self.refreshScroolView() + } +} + +extension CustomConfigSheetController: NSTextFieldDelegate { + func controlTextDidChange(_ obj: Notification) { + if let textField = obj.object as? NSTextField, let identifier = textField.identifier?.rawValue { + let args = identifier.split(separator: Character("-")) + let type = args[0] + let index = Int(args[1])! + let field = String(args[2]) + + if (index < 0) { + return + } + + + let value = textField.stringValue + let trimValue = value.trim() + + if (type == "header") { + self.headers[index][field] = trimValue + } else if (type == "body") { + self.bodys[index][field] = trimValue + } + } + } + + + func controlTextDidEndEditing(_ obj: Notification) { + if let textField = obj.object as? NSTextField, let identifier = textField.identifier?.rawValue { + let args = identifier.split(separator: Character("-")) + let type = args[0] + let index = Int(args[1])! + let field = String(args[2]) + + if (index < 0) { + return + } + + + if (type == "header") { + textField.stringValue = self.headers[index][field] ?? textField.stringValue + } else if (type == "body") { + textField.stringValue = self.bodys[index][field] ?? textField.stringValue + } + } } } diff --git a/uPic/PreferencesWindow/ConfigView/Views/CustomConfigView.swift b/uPic/PreferencesWindow/ConfigView/Views/CustomConfigView.swift index d283e64..f66407f 100644 --- a/uPic/PreferencesWindow/ConfigView/Views/CustomConfigView.swift +++ b/uPic/PreferencesWindow/ConfigView/Views/CustomConfigView.swift @@ -19,6 +19,11 @@ class CustomConfigView: ConfigView { as! CustomConfigSheetController) } + + + deinit { + postConfigSheetController?.removeFromParent() + } override func createView() { @@ -174,6 +179,37 @@ class CustomConfigView: ConfigView { } @objc func openCustomConfigSheet(_ sender: NSButton) { - self.window?.contentViewController?.presentAsSheet(postConfigSheetController!) + guard let data = self.data as? CustomHostConfig else { + return + } + + if let postConfigSheetController = postConfigSheetController { + self.window?.contentViewController?.presentAsSheet(postConfigSheetController) + postConfigSheetController.setData(headerStr: data.headers ?? "", bodyStr: data.bodys ?? "") + self.addCustomConfigObserver() + } + } + + func addCustomConfigObserver() { + PreferencesNotifier.addObserver(observer: self, selector: #selector(saveExtensionsSettings), notification: .saveCustomExtensionSettings) + } + + + func removeCustomConfigObserver() { + PreferencesNotifier.removeObserver(observer: self, notification: .saveCustomExtensionSettings) + } + + @objc func saveExtensionsSettings(notification: Notification) { + self.removeCustomConfigObserver() + guard let userInfo = notification.userInfo else { + print("No userInfo found in notification") + return + } + + let headers = userInfo["headers"] as? String ?? "" + let bodys = userInfo["bodys"] as? String ?? "" + + self.data?.setValue(headers, forKey: "headers") + self.data?.setValue(bodys, forKey: "bodys") } } diff --git a/uPic/Supporting Files/Info.plist b/uPic/Supporting Files/Info.plist index 0f96e09..ed49f83 100644 --- a/uPic/Supporting Files/Info.plist +++ b/uPic/Supporting Files/Info.plist @@ -19,9 +19,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.7.1 + 0.7.2 CFBundleVersion - 20190707 + 20190717 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/uPic/en.lproj/Localizable.strings b/uPic/en.lproj/Localizable.strings index d2234b7..7d08462 100644 --- a/uPic/en.lproj/Localizable.strings +++ b/uPic/en.lproj/Localizable.strings @@ -117,7 +117,7 @@ "host.field.method" = "Method"; "host.field.field" = "File Field"; "host.field.resultPath" = "URL Path"; -"host.field.extensions" = "Fields"; +"host.field.bodys" = "Bodys"; "host.field.headers" = "Headers"; "host.field.owner" = "Owner"; "host.field.repo" = "Repo"; diff --git a/uPic/zh-Hans.lproj/Localizable.strings b/uPic/zh-Hans.lproj/Localizable.strings index a87b9fb..7742b7b 100644 --- a/uPic/zh-Hans.lproj/Localizable.strings +++ b/uPic/zh-Hans.lproj/Localizable.strings @@ -114,7 +114,7 @@ "host.field.method" = "请求方式"; "host.field.field" = "文件字段名"; "host.field.resultPath" = "URL 路径"; -"host.field.extensions" = "扩展字段"; +"host.field.bodys" = "Bodys"; "host.field.headers" = "请求头"; "host.field.owner" = "用户名"; "host.field.repo" = "仓库名";