This commit is contained in:
mrFq1
2023-04-25 14:51:23 +08:00
commit f3141fbffe
39 changed files with 3793 additions and 0 deletions
@@ -0,0 +1,69 @@
//
// RuleItemView.swift
// ClashX Dashboard
//
//
import SwiftUI
struct RuleItemView: View {
@State var index: Int
@State var rule: ClashRule
var body: some View {
HStack(alignment: .center, spacing: 12) {
Text("\(index)")
.font(.system(size: 16))
.foregroundColor(.secondary)
.frame(width: 30)
VStack(alignment: .leading) {
if let payload = rule.payload,
payload != "" {
Text(rule.payload!)
.font(.system(size: 14))
}
HStack() {
Text(rule.type)
.foregroundColor(.secondary)
.frame(width: 120, alignment: .leading)
Text(rule.proxy ?? "")
.foregroundColor({
switch rule.proxy {
case "DIRECT":
return .orange
case "REJECT":
return .red
default:
return .blue
}
}())
}
}
}
}
}
struct RulesRowView_Previews: PreviewProvider {
static var previews: some View {
RuleItemView(index: 114, rule: .init(type: "DIRECT", payload: "cn", proxy: "GeoSite"))
}
}
extension HorizontalAlignment {
private struct RuleItemOBAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context[.leading]
}
}
static let ruleItemOBAlignmentGuide = HorizontalAlignment(
RuleItemOBAlignment.self
)
}
@@ -0,0 +1,38 @@
//
// RuleProviderView.swift
// ClashX Dashboard
//
//
import SwiftUI
struct RuleProviderView: View {
@State var ruleProvider: ClashRuleProvider
var body: some View {
VStack(alignment: .leading) {
HStack {
Text(ruleProvider.name)
.font(.title)
.fontWeight(.medium)
Text(ruleProvider.type)
Text(ruleProvider.behavior)
}
HStack {
Text("\(ruleProvider.ruleCount) rules")
if let date = ruleProvider.updatedAt {
Text("Updated \(RelativeDateTimeFormatter().localizedString(for: date, relativeTo: .now))")
}
}
}
}
}
//struct RuleProviderView_Previews: PreviewProvider {
// static var previews: some View {
// RuleProviderView()
// }
//}
@@ -0,0 +1,67 @@
//
// RulesView.swift
// ClashX Dashboard
//
//
import SwiftUI
struct RulesView: View {
@State var ruleProviders = [ClashRuleProvider]()
@State var ruleItems = [ClashRule]()
@State private var searchString: String = ""
var providers: [ClashRuleProvider] {
if searchString.isEmpty {
return ruleProviders
} else {
return ruleProviders.filtered(searchString, for: ["name", "behavior", "type"])
}
}
var rules: [EnumeratedSequence<[ClashRule]>.Element] {
if searchString.isEmpty {
return Array(ruleItems.enumerated())
} else {
return Array(ruleItems.filtered(searchString, for: ["type", "payload", "proxy"]).enumerated())
}
}
var body: some View {
List {
ForEach(providers, id: \.self) {
RuleProviderView(ruleProvider: $0)
}
ForEach(rules, id: \.element.id) {
RuleItemView(index: $0.offset, rule: $0.element)
}
}
.searchable(text: $searchString)
.onAppear {
ruleItems.removeAll()
ApiRequest.getRules {
ruleItems = $0
}
ApiRequest.requestRuleProviderList {
ruleProviders = $0.allProviders.map {
$0.value
}.sorted {
$0.name < $1.name
}
}
}
}
}
//struct RulesView_Previews: PreviewProvider {
// static var previews: some View {
// RulesView()
// }
//}