diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 373ef89..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "CryptoSwift"] - path = CryptoSwift - url = https://github.com/krzyzanowskim/CryptoSwift.git diff --git a/uPic.xcodeproj/project.pbxproj b/uPic.xcodeproj/project.pbxproj index bd2a316..f185ab3 100644 --- a/uPic.xcodeproj/project.pbxproj +++ b/uPic.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 1647474522B66E3400F9575D /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1647474422B66E3400F9575D /* Util.swift */; }; 164C3A0022B2AA6C003ADE39 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 164C39FF22B2AA6C003ADE39 /* Notifier.swift */; }; 164C3A0222B2AB22003ADE39 /* ConfigNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 164C3A0122B2AB22003ADE39 /* ConfigNotifier.swift */; }; + 165D908622C333740096FF38 /* PasteboardType+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165D908522C333740096FF38 /* PasteboardType+Extension.swift */; }; 1660FCBA22C11C2300372950 /* TencentHostConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1660FCB922C11BA000372950 /* TencentHostConfig.swift */; }; 1660FCBB22C11C7200372950 /* TencentUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1660FCB722C11BA000372950 /* TencentUploader.swift */; }; 1660FCBC22C11C7500372950 /* TencentUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1660FCB822C11BA000372950 /* TencentUtil.swift */; }; @@ -107,6 +108,7 @@ 1647474422B66E3400F9575D /* Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = ""; }; 164C39FF22B2AA6C003ADE39 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = ""; }; 164C3A0122B2AB22003ADE39 /* ConfigNotifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigNotifier.swift; sourceTree = ""; }; + 165D908522C333740096FF38 /* PasteboardType+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PasteboardType+Extension.swift"; sourceTree = ""; }; 1660FCB622C11BA000372950 /* TencentRegion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TencentRegion.swift; sourceTree = ""; }; 1660FCB722C11BA000372950 /* TencentUploader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TencentUploader.swift; sourceTree = ""; }; 1660FCB822C11BA000372950 /* TencentUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TencentUtil.swift; sourceTree = ""; }; @@ -212,6 +214,7 @@ 163632F222B2751D00805E7F /* BoolType.swift */, 164745EF22B65FE900F9575D /* String+Extension.swift */, 1647474222B66ACE00F9575D /* Data+Extension.swift */, + 165D908522C333740096FF38 /* PasteboardType+Extension.swift */, ); path = Basic; sourceTree = ""; @@ -582,6 +585,7 @@ 166B4A5322B9CB4D001288ED /* UpYunConfigView.swift in Sources */, 166B4A5722B9D118001288ED /* PreferencesNotifier.swift in Sources */, 1690E7EB22BF2D9400FC81F8 /* QiniuUploader.swift in Sources */, + 165D908622C333740096FF38 /* PasteboardType+Extension.swift in Sources */, 16A6DC5822AA375700813706 /* AppDelegate.swift in Sources */, 1602ED9722ADEFB200AA8638 /* BaseUploader.swift in Sources */, 163632F322B2751D00805E7F /* BoolType.swift in Sources */, @@ -816,6 +820,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = com.svend.uPic; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; @@ -839,6 +844,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = com.svend.uPic; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; diff --git a/uPic/AppDelegate.swift b/uPic/AppDelegate.swift index 2f0a0f0..bca04f6 100644 --- a/uPic/AppDelegate.swift +++ b/uPic/AppDelegate.swift @@ -127,9 +127,9 @@ extension AppDelegate { if (pasteboardType == NSPasteboard.PasteboardType.png) { let imgData = NSPasteboard.general.data(forType: NSPasteboard.PasteboardType.png) self.uploadFile(nil, data: imgData!) - } else if (pasteboardType == NSPasteboard.PasteboardType.fileURL) { + } else if (pasteboardType == NSPasteboard.PasteboardType.backwardsCompatibleFileURL) { - let filePath = NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.fileURL)! + let filePath = NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.backwardsCompatibleFileURL)! let url = URL(string: filePath)! let fileManager = FileManager.default @@ -175,6 +175,11 @@ extension AppDelegate { /// func uploadCompleted(url: String) { self.setStatusBarIcon(isIndicator: false) + let outputUrl = self.copyUrl(url: url) + NotificationExt.sendUploadSuccessfulNotification(body: outputUrl) + } + + func copyUrl(url: String) -> String { var outputUrl = "" let outputFormat = Defaults[.ouputFormat] switch outputFormat { @@ -186,13 +191,14 @@ extension AppDelegate { break default: outputUrl = url - + } - + NSPasteboard.general.clearContents() NSPasteboard.general.declareTypes([.string], owner: nil) NSPasteboard.general.setString(outputUrl, forType: .string) - NotificationExt.sendUploadSuccessfulNotification(body: outputUrl) + + return outputUrl } /// @@ -253,4 +259,3 @@ extension AppDelegate: NSWindowDelegate, NSDraggingDestination { } } - diff --git a/uPic/Base.lproj/Main.storyboard b/uPic/Base.lproj/Main.storyboard index 556d22f..0860cd7 100644 --- a/uPic/Base.lproj/Main.storyboard +++ b/uPic/Base.lproj/Main.storyboard @@ -684,6 +684,7 @@ + @@ -744,6 +745,11 @@ + + + + + diff --git a/uPic/Basic/Data+Extension.swift b/uPic/Basic/Data+Extension.swift index 1510f59..6983a2c 100644 --- a/uPic/Basic/Data+Extension.swift +++ b/uPic/Basic/Data+Extension.swift @@ -47,12 +47,4 @@ extension Data { return String(data: self, encoding: .utf8)! } - init(from value: T) { - var value = value - self.init(buffer: UnsafeBufferPointer(start: &value, count: 1)) - } - - func to(type: T.Type) -> T { - return self.withUnsafeBytes { $0.pointee } - } } diff --git a/uPic/Basic/PasteboardType+Extension.swift b/uPic/Basic/PasteboardType+Extension.swift new file mode 100644 index 0000000..152464c --- /dev/null +++ b/uPic/Basic/PasteboardType+Extension.swift @@ -0,0 +1,25 @@ +// +// PasteboardType+Extension.swift +// uPic +// +// Created by Svend Jin on 2019/6/26. +// Copyright © 2019 Svend Jin. All rights reserved. +// + +import Cocoa + + +extension NSPasteboard.PasteboardType { + // MARK: 剪切板扩展,让 10.13 以前的版本也支持 FileUrl 类型 + + static let backwardsCompatibleFileURL: NSPasteboard.PasteboardType = { + + if #available(OSX 10.13, *) { + return NSPasteboard.PasteboardType.fileURL + } else { + return NSPasteboard.PasteboardType(kUTTypeFileURL as String) + } + + } () + +} diff --git a/uPic/Extensions/NotificationExt.swift b/uPic/Extensions/NotificationExt.swift index 3d509e7..3d6bec2 100644 --- a/uPic/Extensions/NotificationExt.swift +++ b/uPic/Extensions/NotificationExt.swift @@ -11,37 +11,12 @@ import UserNotifications class NotificationExt: NSObject { static let shared = NotificationExt() - - // MARK: 本地通知扩展 - + func sendNotification(title: String, subTitle: String, body: String) -> Void { - let content = UNMutableNotificationContent() - content.title = title - content.subtitle = subTitle - content.body = body - - content.sound = UNNotificationSound.default - content.categoryIdentifier = "U_PIC" - content.userInfo = ["url": body] - - let request = UNNotificationRequest(identifier: "U_PIC_REQUEST", - content: content, - trigger: nil) - - - let category = UNNotificationCategory(identifier: "U_PIC_CATEGORY", - actions: [], - intentIdentifiers: [], - hiddenPreviewsBodyPlaceholder: "", - options: .customDismissAction) - - let notificationCenter = UNUserNotificationCenter.current() - notificationCenter.delegate = self - notificationCenter.setNotificationCategories([category]) - notificationCenter.add(request) { (error) in - if error != nil { - // Handle any errors. - } + if #available(OSX 10.14, *) { + self.sendNotificationByNew(title: title, subTitle: subTitle, body: body) + } else { + self.sendNotificationByOld(title: title, subTitle: subTitle, body: body) } } @@ -59,6 +34,14 @@ class NotificationExt: NSObject { NotificationExt.shared.sendNotification(title: NSLocalizedString("upload.notification.success.title", comment: "上传成功通知标题"), subTitle: NSLocalizedString("upload.notification.success.subtitle", comment: "上传成功通知副标题"), body: body!) } + + /// + /// 发送拷贝成功通知 + /// + static func sendCopySuccessfulNotification(body: String? = "") { + + NotificationExt.shared.sendNotification(title: "", subTitle: NSLocalizedString("upload.notification.success.subtitle", comment: "上传成功通知副标题"), body: body!) + } /// @@ -79,16 +62,52 @@ class NotificationExt: NSObject { } +@available(OSX 10.14, *) extension NotificationExt: UNUserNotificationCenterDelegate { + + + // MARK: Version Target >= 10.14 + + func sendNotificationByNew(title: String, subTitle: String, body: String) -> Void { + let content = UNMutableNotificationContent() + content.title = title + content.subtitle = subTitle + content.body = body + + content.sound = UNNotificationSound.default + content.categoryIdentifier = "NOTIFICATION_U_PIC" + content.userInfo = ["body": body] + + let request = UNNotificationRequest(identifier: "U_PIC_REQUEST", + content: content, + trigger: nil) + + + let category = UNNotificationCategory(identifier: "U_PIC_CATEGORY", + actions: [], + intentIdentifiers: [], + hiddenPreviewsBodyPlaceholder: "", + options: .customDismissAction) + + let notificationCenter = UNUserNotificationCenter.current() + notificationCenter.delegate = self + notificationCenter.setNotificationCategories([category]) + notificationCenter.add(request) { (error) in + if error != nil { + // Handle any errors. + } + } + + } // 用户点击弹窗后的回调 func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo - if let url = userInfo["url"] { + if let body = userInfo["body"] { NSPasteboard.general.clearContents() NSPasteboard.general.declareTypes([.string], owner: nil) - NSPasteboard.general.setString(url as! String, forType: .string) + NSPasteboard.general.setString(body as! String, forType: .string) } completionHandler() @@ -99,3 +118,50 @@ extension NotificationExt: UNUserNotificationCenterDelegate { completionHandler([.alert, .sound]) } } + +extension NotificationExt: NSUserNotificationCenterDelegate { + + // MARK: Version Target < 10.14 + + func sendNotificationByOld(title: String, subTitle: String, body: String) -> Void { + + let userNotification = NSUserNotification() + + userNotification.title = title + userNotification.subtitle = subTitle + userNotification.informativeText = body + + userNotification.identifier = "OLD_NOTIFICATION_U_PIC" + userNotification.userInfo = ["body": "body"] + + userNotification.soundName = NSUserNotificationDefaultSoundName + + NSUserNotificationCenter.default.delegate = self + NSUserNotificationCenter.default.removeAllDeliveredNotifications() + NSUserNotificationCenter.default.deliver(userNotification) + + } + + // 当 App 在前台时是否弹出通知 + func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool { + return true + } + + // 推送消息后的回调 + func userNotificationCenter(_ center: NSUserNotificationCenter, didDeliver notification: NSUserNotification) { + print("\(Date(timeIntervalSinceNow: 0)) -> 消息已经推送") + } + + // 用户点击了通知后的回调 + func userNotificationCenter(_ center: NSUserNotificationCenter, didActivate notification: NSUserNotification) { + + if notification.activationType == .contentsClicked { + if let userInfo = notification.userInfo, let body = userInfo["body"] { + NSPasteboard.general.clearContents() + NSPasteboard.general.declareTypes([.string], owner: nil) + NSPasteboard.general.setString(body as! String, forType: .string) + } + } + } + +} diff --git a/uPic/General/Managers/ConfigManager.swift b/uPic/General/Managers/ConfigManager.swift index 9ff0af0..d47a1ae 100644 --- a/uPic/General/Managers/ConfigManager.swift +++ b/uPic/General/Managers/ConfigManager.swift @@ -33,8 +33,7 @@ public class ConfigManager { Defaults[.launchAtLogin] = newValue?.rawValue } } - - + public func firstSetup() { guard firstUsage == ._true else { return @@ -53,16 +52,23 @@ public class ConfigManager { Defaults.synchronize() } +} + + +extension ConfigManager { + // MARK: 图床配置和默认图床 + func getHostItems() -> [Host] { return Defaults[.hostItems] ?? [Host]() } - + func setHostItems(items: [Host]) -> Void { Defaults[.hostItems] = items + Defaults.synchronize() ConfigNotifier.postNotification(.changeHostItems) } - - + + func getDefaultHost() -> Host? { guard let defaultHostId = Defaults[.defaultHostId], let hostItems = Defaults[.hostItems] else { return nil @@ -74,5 +80,49 @@ public class ConfigManager { } return nil } +} + +extension ConfigManager { + // MARK: 上传历史 + + public var historyLimit: Int { + get { + let defaultLimit = 10 + let limit = Defaults[.historyLimit] + if (limit == nil || limit == 0) { + return defaultLimit + } + return limit! + } + + set { + Defaults[.historyLimit] = newValue + } + } + + func getHistoryList() -> [String] { + return Defaults[.historyList] ?? [String]() + } + + func setHistoryList(items: [String]) -> Void { + Defaults[.historyList] = items + Defaults.synchronize() + ConfigNotifier.postNotification(.changeHistoryList) + } + + func addHistory(url: String) -> Void { + var list = self.getHistoryList() + list.append(url) + + if list.count > self.historyLimit { + list.removeFirst(list.count - self.historyLimit) + } + + self.setHistoryList(items: list) + } + + func clearHistoryList() -> Void { + self.setHistoryList(items: []) + } } diff --git a/uPic/General/Utils/PreferenceKey.swift b/uPic/General/Utils/PreferenceKey.swift index a6338f7..4e525e7 100644 --- a/uPic/General/Utils/PreferenceKey.swift +++ b/uPic/General/Utils/PreferenceKey.swift @@ -14,6 +14,8 @@ struct Keys { static let hostItems = "uPic_hostItems" static let defaultHostId = "uPic_DefaultHostId" static let ouputFormat = "uPic_OutputFormat" + static let historyList = "uPic_HistoryList" + static let historyLimit = "uPic_HistoryLimit" } class DefaultsKeys { @@ -41,6 +43,8 @@ extension DefaultsKeys { static let hostItems = DefaultsKey<[Host]>(Keys.hostItems) static let defaultHostId = DefaultsKey(Keys.defaultHostId) static let ouputFormat = DefaultsKey(Keys.ouputFormat) + static let historyList = DefaultsKey<[String]>(Keys.historyList) + static let historyLimit = DefaultsKey(Keys.historyLimit) } @@ -88,4 +92,13 @@ extension UserDefaults { set(result, forKey: key._key) } } + + subscript(key: DefaultsKey<[String]>) -> [String]? { + get { + return array(forKey: key._key) as? [String] + } + set { + set(newValue, forKey: key._key) + } + } } diff --git a/uPic/Models/BaseUploader.swift b/uPic/Models/BaseUploader.swift index c59bc6a..f59a163 100644 --- a/uPic/Models/BaseUploader.swift +++ b/uPic/Models/BaseUploader.swift @@ -116,6 +116,7 @@ class BaseUploader { } func completed(url: String) { + ConfigManager.shared.addHistory(url: url) (NSApplication.shared.delegate as? AppDelegate)?.uploadCompleted(url: url) } diff --git a/uPic/Models/UpYun/UpYunUploader.swift b/uPic/Models/UpYun/UpYunUploader.swift index 8870f90..118494a 100644 --- a/uPic/Models/UpYun/UpYunUploader.swift +++ b/uPic/Models/UpYun/UpYunUploader.swift @@ -48,7 +48,7 @@ class UpYunUploader: BaseUploader { var saveKey = fileName if (config.folder != nil && config.folder!.count > 0) { - saveKey = "/\(config.folder!)/\(saveKey)" + saveKey = "\(config.folder!)\(saveKey)" } // MARK: 加密 policy @@ -87,7 +87,7 @@ class UpYunUploader: BaseUploader { let json = JSON(value) let code = json["code"] if 200 == code { - super.completed(url: domain + saveKey) + super.completed(url: "\(domain)/\(saveKey)") } else { super.faild(errorMsg: json["message"].string) } diff --git a/uPic/Notifier/ConfigNotifier.swift b/uPic/Notifier/ConfigNotifier.swift index 6268150..35073cd 100644 --- a/uPic/Notifier/ConfigNotifier.swift +++ b/uPic/Notifier/ConfigNotifier.swift @@ -12,6 +12,7 @@ public class ConfigNotifier: Notifier { public enum Notification: String { case changeHostItems + case changeHistoryList } } diff --git a/uPic/StatusMenuController.swift b/uPic/StatusMenuController.swift index 15fd3aa..7fa6af3 100644 --- a/uPic/StatusMenuController.swift +++ b/uPic/StatusMenuController.swift @@ -18,6 +18,7 @@ class StatusMenuController: NSObject, NSMenuDelegate { @IBOutlet weak var screenshotMenuItem: NSMenuItem! @IBOutlet weak var hostMenuItem: NSMenuItem! @IBOutlet weak var ouputFormatMenuItem: NSMenuItem! + @IBOutlet weak var historyMenuItem: NSMenuItem! @IBOutlet weak var preferenceMenuItem: NSMenuItem! @IBOutlet weak var helpMenuItem: NSMenuItem! @IBOutlet weak var checkUpdateMenuItem: NSMenuItem! @@ -33,11 +34,13 @@ class StatusMenuController: NSObject, NSMenuDelegate { screenshotMenuItem.title = NSLocalizedString("status-menu.screenshot", comment: "Upload with pasteboard") 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") checkUpdateMenuItem.title = NSLocalizedString("status-menu.check-update", comment: "Check update") quitMenuItem.title = NSLocalizedString("status-menu.quit", comment: "Quit") resetHostMenu() + resetUploadHistory() refreshOutputFormat() addObserver() } @@ -85,9 +88,7 @@ class StatusMenuController: NSObject, NSMenuDelegate { } @objc func resetHostMenu() { - guard let hostItems = Defaults[.hostItems] else { - return - } + let hostItems = ConfigManager.shared.getHostItems() hostMenuItem.submenu?.removeAllItems() for item in hostItems { let menuItem = NSMenuItem(title: item.name, action: #selector(changeDefaultHost(_:)), keyEquivalent: "") @@ -100,6 +101,38 @@ class StatusMenuController: NSObject, NSMenuDelegate { } self.refreshDefaultHost() } + + @objc func resetUploadHistory() { + let historyList = ConfigManager.shared.getHistoryList() + + historyMenuItem.submenu?.removeAllItems() + for url in historyList { + let menuItem = NSMenuItem(title: url, action: #selector(copyUrl(_:)), keyEquivalent: "") + menuItem.target = self + historyMenuItem.submenu?.addItem(menuItem) + historyMenuItem.submenu?.delegate = self + } + + if ((historyMenuItem.submenu?.items.count ?? 0) > 0) { + historyMenuItem.submenu?.addItem(NSMenuItem.separator()) + let menuItem = NSMenuItem(title: NSLocalizedString("status-menu.upload-history-clear", comment: "clear history"), action: #selector(clearHistory(_:)), keyEquivalent: "") + menuItem.target = self + historyMenuItem.submenu?.addItem(menuItem) + } else { + let menuItem = NSMenuItem(title: NSLocalizedString("status-menu.upload-history-empty", comment: "history is empty"), action: nil, keyEquivalent: "") + menuItem.target = self + historyMenuItem.submenu?.addItem(menuItem) + } + } + + @objc func copyUrl(_ sender: NSMenuItem) { + let outputUrl = (NSApplication.shared.delegate as? AppDelegate)?.copyUrl(url: sender.title) + NotificationExt.sendCopySuccessfulNotification(body: outputUrl) + } + + @objc func clearHistory(_ sender: NSMenuItem) { + ConfigManager.shared.clearHistoryList() + } func refreshDefaultHost() { let outputFormat = Defaults[.defaultHostId] @@ -138,5 +171,11 @@ class StatusMenuController: NSObject, NSMenuDelegate { func addObserver() { ConfigNotifier.addObserver(observer: self, selector: #selector(resetHostMenu), notification: .changeHostItems) + ConfigNotifier.addObserver(observer: self, selector: #selector(resetUploadHistory), notification: .changeHistoryList) + } + + func removeObserver() { + ConfigNotifier.removeObserver(observer: self, notification: .changeHostItems) + ConfigNotifier.removeObserver(observer: self, notification: .changeHistoryList) } } diff --git a/uPic/en.lproj/Localizable.strings b/uPic/en.lproj/Localizable.strings index a8140e9..cb1797d 100644 --- a/uPic/en.lproj/Localizable.strings +++ b/uPic/en.lproj/Localizable.strings @@ -66,6 +66,15 @@ /* 菜单栏检查更新按钮标题:检查更新 */ "status-menu.check-update" = "Check for updates"; +/* 菜单栏上传历史 */ +"status-menu.upload-history" = "Upload history"; + +/* 菜单栏清空上传历史 */ +"status-menu.upload-history-clear" = "Clear upload history"; + +/* 菜单栏没有上传历史 */ +"status-menu.upload-history-empty" = "No upload history"; + /* 前往下载 */ "check-update-tip-get-gobutton.title" = "Download"; diff --git a/uPic/zh-Hans.lproj/Localizable.strings b/uPic/zh-Hans.lproj/Localizable.strings index 5f6d607..465f0a9 100644 --- a/uPic/zh-Hans.lproj/Localizable.strings +++ b/uPic/zh-Hans.lproj/Localizable.strings @@ -64,6 +64,15 @@ /* 菜单栏检查更新按钮标题:检查更新 */ "status-menu.check-update" = "检查更新"; +/* 菜单栏上传历史 */ +"status-menu.upload-history" = "上传历史"; + +/* 菜单栏清空上传历史 */ +"status-menu.upload-history-clear" = "清空上传历史"; + +/* 菜单栏没有上传历史 */ +"status-menu.upload-history-empty" = "暂无上传历史"; + /* 前往下载 */ "check-update-tip-get-gobutton.title" = "前往下载";