ClashX.Meta/ClashX/ViewControllers/RemoteConfigViewController....

260 lines
8.9 KiB
Swift

//
// RemoteConfigViewController.swift
// ClashX
//
// Created by yicheng on 2019/7/28.
// Copyright © 2019 west2online. All rights reserved.
//
import Cocoa
import RxSwift
class RemoteConfigViewController: NSViewController {
@IBOutlet var tableView: NSTableView!
@IBOutlet var deleteButton: NSButton!
@IBOutlet var updateButton: NSButton!
private var latestAddedConfig: RemoteConfigModel?
let disposeBag = DisposeBag()
deinit {
print("RemoteConfigViewController deinit")
}
override func viewDidLoad() {
super.viewDidLoad()
updateButtonStatus()
tableView.doubleAction = #selector(tableViewDidDoubleClick(tableView:))
NotificationCenter.default
.rx.notification(Notification.Name("didGetUrl")).bind {
[weak self] note in
guard let self = self else { return }
guard let url = note.userInfo?["url"] as? String else { return }
let name = note.userInfo?["name"] as? String
self.showAdd(defaultUrl: url, name: name, allowAlt: true)
}.disposed(by: disposeBag)
}
override func viewWillAppear() {
super.viewWillAppear()
view.window?.level = .floating
NSApp.activate(ignoringOtherApps: true)
}
override func viewWillDisappear() {
super.viewWillDisappear()
RemoteConfigManager.shared.saveConfigs()
}
// MARK: Actions
@IBAction func actionAdd(_ sender: Any) {
showAdd()
}
@IBAction func actionDelete(_ sender: Any) {
RemoteConfigManager.shared.configs.safeRemove(at: tableView.selectedRow)
tableView.reloadData()
updateButtonStatus()
}
@IBAction func actionUpdate(_ sender: Any) {
guard let model = RemoteConfigManager.shared.configs[safe: tableView.selectedRow] else { return }
requestUpdate(config: model)
tableView.reloadDataKeepingSelection()
}
}
extension RemoteConfigViewController {
func updateButtonStatus() {
let selectIdx = tableView.selectedRow
if selectIdx == -1 {
deleteButton.isEnabled = false
updateButton.isEnabled = false
return
}
guard let config = RemoteConfigManager.shared.configs[safe: selectIdx] else { return }
deleteButton.isEnabled = true
updateButton.isEnabled = !config.updating
}
func showAdd(defaultUrl: String? = nil,
defaultName: String? = nil,
name: String? = nil,
allowAlt: Bool = false) {
let alertView = NSAlert()
alertView.addButton(withTitle: NSLocalizedString("OK", comment: ""))
alertView.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
alertView.messageText = NSLocalizedString("Add a remote config", comment: "")
let remoteConfigInputView = RemoteConfigAddView.createFromNib()
if let defaultUrl = defaultUrl {
remoteConfigInputView.setUrl(string: defaultUrl, name: name, defaultName: defaultName)
}
alertView.accessoryView = remoteConfigInputView
let response = alertView.runModal()
guard response == .alertFirstButtonReturn else { return }
guard remoteConfigInputView.isVaild() else {
let alert = NSAlert()
alert.messageText = NSLocalizedString("Invalid input", comment: "")
alert.alertStyle = .warning
alert.runModal()
return
}
let configName = remoteConfigInputView.getConfigName().0
let isPlaceHolderName = remoteConfigInputView.getConfigName().1
let configUrl = remoteConfigInputView.getUrlString()
if let existed = RemoteConfigManager.shared.configs.first(where: { $0.name == configName }) {
guard allowAlt else {
NSAlert.alert(with: NSLocalizedString("The remote config name is duplicated", comment: ""))
return
}
existed.url = configUrl
latestAddedConfig = existed
requestUpdate(config: existed)
} else {
let remoteConfig = RemoteConfigModel(url: configUrl,
name: configName,
updateTime: nil)
remoteConfig.isPlaceHolderName = !isPlaceHolderName
RemoteConfigManager.shared.configs.append(remoteConfig)
requestUpdate(config: remoteConfig)
latestAddedConfig = remoteConfig
}
tableView.reloadData()
updateButtonStatus()
}
func requestUpdate(config: RemoteConfigModel) {
guard !config.updating else { return }
config.updating = true
RemoteConfigManager.updateConfig(config: config) {
[weak self, weak config] errorString in
guard let self = self, let config = config else { return }
config.updating = false
if let errorString = errorString {
let alert = NSAlert()
alert.messageText = errorString
alert.alertStyle = .warning
alert.runModal()
} else {
config.updateTime = Date()
RemoteConfigManager.shared.saveConfigs()
if config == self.latestAddedConfig {
AppDelegate.shared.updateConfig(configName: config.name)
} else if config.name == ConfigManager.selectConfigName {
AppDelegate.shared.updateConfig()
}
}
self.tableView.reloadDataKeepingSelection()
}
}
}
extension RemoteConfigViewController: NSTableViewDelegate {
func tableViewSelectionDidChange(_ notification: Notification) {
updateButtonStatus()
}
@objc func tableViewDidDoubleClick(tableView: NSTableView) {
let row = tableView.clickedRow
guard let config = RemoteConfigManager.shared.configs[safe: row] else { return }
if config.isPlaceHolderName {
showAdd(defaultUrl: config.url, defaultName: config.name, name: nil, allowAlt: true)
} else {
showAdd(defaultUrl: config.url, defaultName: nil, name: config.name, allowAlt: true)
}
}
}
extension RemoteConfigViewController: NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return RemoteConfigManager.shared.configs.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
guard let config = RemoteConfigManager.shared.configs[safe: row] else { return nil }
func setupCell(withIdentifier: String, string: String, textFieldtag: Int = 1) -> NSView? {
let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: withIdentifier), owner: nil)
if let textField = cell?.viewWithTag(1) as? NSTextField {
textField.stringValue = string
} else {
assertionFailure()
}
return cell
}
switch tableColumn?.identifier.rawValue ?? "" {
case "url":
return setupCell(withIdentifier: "urlCell", string: config.url)
case "configName":
return setupCell(withIdentifier: "nameCell", string: config.name)
case "updateTime":
return setupCell(withIdentifier: "timeCell", string: config.displayingTimeString())
default: assertionFailure()
}
return nil
}
}
class RemoteConfigAddView: NSView, NibLoadable {
@IBOutlet private var urlTextField: NSTextField!
@IBOutlet private var configNameTextField: NSTextField!
func getUrlString() -> String {
return urlTextField.stringValue
}
/// Get the config name
/// - Returns: return (name, isUserInput)
func getConfigName() -> (String, Bool) {
if configNameTextField.stringValue.count > 0 {
return (configNameTextField.stringValue, true)
}
return (configNameTextField.placeholderString ?? "", false)
}
func isVaild() -> Bool {
return urlTextField.stringValue.isUrlVaild() && getConfigName().0.count > 0
}
func setUrl(string: String, name: String? = nil, defaultName: String?) {
urlTextField.stringValue = string
if let name = name, name.count > 0 {
configNameTextField.stringValue = name
}
if let defaultName = defaultName, defaultName.count > 0 {
configNameTextField.placeholderString = defaultName
}
if name == nil && defaultName == nil {
updateConfigName()
}
}
private func updateConfigName() {
guard urlTextField.stringValue.isUrlVaild() else { return }
let urlString = urlTextField.stringValue
configNameTextField.placeholderString = URL(string: urlString)?.host ?? "unknown"
}
}
extension RemoteConfigAddView: NSTextFieldDelegate {
func controlTextDidChange(_ obj: Notification) {
updateConfigName()
}
}