149 lines
4.6 KiB
Swift
149 lines
4.6 KiB
Swift
import AppKit
|
|
import Foundation
|
|
import SwiftUI
|
|
import MacToolsPluginKit
|
|
|
|
public final class IPOverviewPluginFactory: NSObject, MacToolsPluginBundleFactory {
|
|
public static func makeProvider(context: PluginRuntimeContext) throws -> any PluginProvider {
|
|
IPOverviewPluginProvider(context: context)
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
private struct IPOverviewPluginProvider: PluginProvider {
|
|
let context: PluginRuntimeContext
|
|
|
|
func makePlugins() -> [any MacToolsPlugin] {
|
|
[IPOverviewPlugin(context: context, localization: PluginLocalization(bundle: context.resourceBundle))]
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
final class IPOverviewPlugin: MacToolsPlugin, PluginPrimaryPanel, PluginConfigurationPresenting {
|
|
enum ControlID {
|
|
static let openSettings = "execute"
|
|
static let copyIP = "ip-overview-copy-ip"
|
|
}
|
|
|
|
let metadata: PluginMetadata
|
|
|
|
let primaryPanelDescriptor: PluginPrimaryPanelDescriptor
|
|
|
|
private let viewModel: IPOverviewViewModel
|
|
private let localization: PluginLocalization
|
|
|
|
var onStateChange: (() -> Void)?
|
|
var requestPermissionGuidance: ((String) -> Void)?
|
|
var shortcutBindingResolver: ((String) -> ShortcutBinding?)?
|
|
var requestConfigurationPresentation: (() -> Void)?
|
|
|
|
init(
|
|
context: PluginRuntimeContext = PluginRuntimeContext(pluginID: "ip-overview"),
|
|
viewModel: IPOverviewViewModel? = nil,
|
|
localization: PluginLocalization = PluginLocalization(bundle: .main)
|
|
) {
|
|
self.localization = localization
|
|
self.viewModel = viewModel ?? IPOverviewViewModel(storage: context.storage, localization: localization)
|
|
self.metadata = PluginMetadata(
|
|
id: "ip-overview",
|
|
title: localization.string("metadata.title", defaultValue: "IP 检测"),
|
|
iconName: "network",
|
|
iconTint: Color(nsColor: .systemBlue),
|
|
order: 12,
|
|
defaultDescription: localization.string(
|
|
"metadata.description",
|
|
defaultValue: "查看公网 IP、本地地址和归属地"
|
|
)
|
|
)
|
|
self.primaryPanelDescriptor = PluginPrimaryPanelDescriptor(
|
|
controlStyle: .button,
|
|
menuActionBehavior: .dismissBeforeHandling,
|
|
buttonTitleProvider: { localization.string("panel.button.check", defaultValue: "检测") }
|
|
)
|
|
self.viewModel.onSnapshotChange = { [weak self] in
|
|
self?.onStateChange?()
|
|
}
|
|
}
|
|
|
|
func activate(context: PluginRuntimeContext) {
|
|
viewModel.refreshIfNeeded()
|
|
}
|
|
|
|
func deactivate(reason: PluginDeactivationReason) {
|
|
viewModel.cancel()
|
|
}
|
|
|
|
func refresh() {
|
|
viewModel.refreshPublicIP()
|
|
}
|
|
|
|
var primaryPanelState: PluginPanelState {
|
|
PluginPanelState(
|
|
subtitle: panelSubtitle,
|
|
isOn: viewModel.snapshot.isRefreshing,
|
|
isExpanded: false,
|
|
isEnabled: true,
|
|
isVisible: true,
|
|
detail: nil,
|
|
errorMessage: viewModel.snapshot.errorMessage
|
|
)
|
|
}
|
|
|
|
var configuration: PluginConfiguration? {
|
|
PluginConfiguration(
|
|
description: metadata.defaultDescription,
|
|
prefersFullHeight: true
|
|
) { _ in
|
|
IPOverviewComponentView(
|
|
viewModel: self.viewModel,
|
|
localization: self.localization,
|
|
startsInDetails: true,
|
|
showsBackButton: false
|
|
)
|
|
}
|
|
}
|
|
|
|
func handleAction(_ action: PluginPanelAction) {
|
|
switch action {
|
|
case let .invokeAction(controlID):
|
|
handleInvoke(controlID: controlID)
|
|
case .setDisclosureExpanded,
|
|
.setSwitch,
|
|
.setSelection,
|
|
.setNavigationSelection,
|
|
.clearNavigationSelection,
|
|
.setDate,
|
|
.setSlider:
|
|
break
|
|
}
|
|
}
|
|
|
|
private var panelSubtitle: String {
|
|
let snapshot = viewModel.snapshot
|
|
if snapshot.isRefreshing {
|
|
return localization.string("panel.subtitle.refreshing", defaultValue: "正在检测公网 IP...")
|
|
}
|
|
|
|
if let ip = snapshot.preferredPublicIP?.ip {
|
|
return viewModel.hidesSensitiveInfo ? IPOverviewSensitiveValueMask.maskedIP(ip) : ip
|
|
}
|
|
|
|
if let errorMessage = snapshot.errorMessage {
|
|
return errorMessage
|
|
}
|
|
|
|
return metadata.defaultDescription
|
|
}
|
|
|
|
private func handleInvoke(controlID: String) {
|
|
switch controlID {
|
|
case ControlID.openSettings:
|
|
requestConfigurationPresentation?()
|
|
case ControlID.copyIP:
|
|
viewModel.copy(viewModel.snapshot.preferredPublicIP?.ip)
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|