forked from SagerNet/sing-box-for-apple
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWidgetExtension.swift
89 lines (76 loc) · 2.71 KB
/
WidgetExtension.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import AppIntents
import Libbox
import Library
import SwiftUI
import WidgetKit
struct Provider: AppIntentTimelineProvider {
func placeholder(in _: Context) -> ExtensionStatus {
ExtensionStatus(date: .now, isConnected: false, profileList: [])
}
func snapshot(for _: ConfigurationIntent, in _: Context) async -> ExtensionStatus {
var status = ExtensionStatus(date: .now, isConnected: false, profileList: [])
do {
status.isConnected = try await ExtensionProfile.load()?.status.isStrictConnected ?? false
let profileList = try ProfileManager.list()
let selectedProfileID = SharedPreferences.selectedProfileID
for profile in profileList {
status.profileList.append(ProfileEntry(profile: profile, isSelected: profile.id == selectedProfileID))
}
} catch {}
return status
}
func timeline(for intent: ConfigurationIntent, in context: Context) async -> Timeline<ExtensionStatus> {
await Timeline(entries: [snapshot(for: intent, in: context)], policy: .never)
}
}
struct ExtensionStatus: TimelineEntry {
var date: Date
var isConnected: Bool
var profileList: [ProfileEntry]
}
struct ProfileEntry {
let profile: Profile
let isSelected: Bool
}
struct WidgetView: View {
@Environment(\.widgetFamily) private var family
var status: ExtensionStatus
var body: some View {
VStack {
LabeledContent {
Text(LibboxVersion())
.font(.caption)
} label: {
Text("sing-box")
.font(.headline)
}
VStack {
viewBuilder {
if !status.isConnected {
Button(intent: StartServiceIntent()) {
Image(systemName: "play.fill")
}
} else {
Button(intent: StopServiceIntent()) {
Image(systemName: "stop.fill")
}
}
}
.controlSize(.large)
.invalidatableContent()
}
.frame(maxHeight: .infinity, alignment: .center)
}
.frame(maxHeight: .infinity, alignment: .topLeading)
.containerBackground(.fill.tertiary, for: .widget)
}
}
struct WidgetExtension: Widget {
@State private var extensionProfile: ExtensionProfile?
var body: some WidgetConfiguration {
AppIntentConfiguration(kind: "sing-box", intent: ConfigurationIntent.self, provider: Provider()) { status in
WidgetView(status: status)
}
.supportedFamilies([.systemSmall])
}
}