Add preview support

This commit is contained in:
世界
2023-07-20 13:15:13 +08:00
parent fdbf50d875
commit 251f441a25
14 changed files with 135 additions and 59 deletions
@@ -2,4 +2,5 @@ import Foundation
public class ApplicationLibrary {
public static let bundle = Bundle(for: ApplicationLibrary.self)
public static let inPreview = false
}
@@ -36,7 +36,7 @@ public struct ActiveDashboardView: View {
} else {
VStack {
#if os(iOS)
if profile.status.isConnected {
if ApplicationLibrary.inPreview || profile.status.isConnected {
ExtensionStatusView()
.listStyle(.automatic)
.navigationBarTitleDisplayMode(.inline)
@@ -53,7 +53,7 @@ public struct ActiveDashboardView: View {
}
}
#else
if profile.status.isConnected {
if ApplicationLibrary.inPreview || profile.status.isConnected {
ExtensionStatusView()
}
FormView {
@@ -74,7 +74,7 @@ public struct ActiveDashboardView: View {
await switchProfile(selectedProfileID!)
}
}
.disabled(!profile.status.isSwitchable || reasserting)
.disabled(!ApplicationLibrary.inPreview && (!profile.status.isSwitchable || reasserting))
}
}
}
@@ -115,24 +115,32 @@ public struct ActiveDashboardView: View {
defer {
isLoading = false
}
do {
profileList = try ProfileManager.list()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
return
}
if profileList.isEmpty {
return
}
if ApplicationLibrary.inPreview {
profileList = [
Profile(id: 0, name: "profile local", type: .local, path: ""),
Profile(id: 1, name: "profile remote", type: .remote, path: "", lastUpdated: Date(timeIntervalSince1970: 0)),
]
selectedProfileID = 0
} else {
do {
profileList = try ProfileManager.list()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
return
}
if profileList.isEmpty {
return
}
selectedProfileID = SharedPreferences.selectedProfileID
if profileList.filter({ profile in
profile.id == selectedProfileID
})
.isEmpty {
selectedProfileID = profileList[0].id!
SharedPreferences.selectedProfileID = selectedProfileID
selectedProfileID = SharedPreferences.selectedProfileID
if profileList.filter({ profile in
profile.id == selectedProfileID
})
.isEmpty {
selectedProfileID = profileList[0].id!
SharedPreferences.selectedProfileID = selectedProfileID
}
}
}
@@ -7,7 +7,9 @@ public struct DashboardView: View {
public var body: some View {
viewBuilder {
if let profile = extensionProfile.wrappedValue {
if ApplicationLibrary.inPreview {
ActiveDashboardView()
} else if let profile = extensionProfile.wrappedValue {
ActiveDashboardView().environmentObject(profile)
} else {
FormView {
@@ -18,7 +18,16 @@ public struct ExtensionStatusView: View {
viewBuilder {
VStack {
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: columnCount), alignment: .leading) {
if let message {
if ApplicationLibrary.inPreview {
StatusItem("Memory", "6.4 MiB")
StatusItem("Goroutines", "89")
StatusItem("Inbound Connections", "34")
StatusItem("Outbound Connections", "28")
StatusItem("Uplink", "38 B/s")
StatusItem("Downlink", "249 MiB/s")
StatusItem("Uplink Total", "52 MiB")
StatusItem("Downlink Total", "5.6 GiB")
} else if let message {
StatusItem("Memory", LibboxFormatBytes(message.memory))
StatusItem("Goroutines", "\(message.goroutines)")
if message.trafficAvailable {
@@ -9,7 +9,18 @@ public struct StartStopButton: View {
public var body: some View {
viewBuilder {
if let profile = extensionProfile.wrappedValue {
if ApplicationLibrary.inPreview {
#if os(iOS)
Toggle(isOn: .constant(true)) {
Text("Enabled")
}
#elseif os(macOS)
Button(action: {}, label: {
Label("Stop", systemImage: "stop.fill")
})
#endif
} else if let profile = extensionProfile.wrappedValue {
Button0(profile)
} else {
#if os(iOS)
@@ -17,6 +28,7 @@ public struct StartStopButton: View {
Text("Enabled")
}
#elseif os(macOS)
Button(action: {}, label: {
Label("Start", systemImage: "play.fill")
})
@@ -43,9 +43,24 @@ public struct GroupListView: View {
}
private func doReload() {
connectTask?.cancel()
connectTask = Task.detached {
await connect()
if ApplicationLibrary.inPreview {
groups = [
OutboundGroup(tag: "my_group", type: "selector", selected: "server", selectable: true, items: [
OutboundGroupItem(tag: "server", type: "Shadowsocks", urlTestTime: .now, urlTestDelay: 12),
OutboundGroupItem(tag: "server2", type: "WireGuard", urlTestTime: .now, urlTestDelay: 34),
OutboundGroupItem(tag: "auto", type: "URLTest", urlTestTime: .now, urlTestDelay: 100),
]),
OutboundGroup(tag: "group2", type: "urltest", selected: "client", selectable: false, items:
(0 ..< 234).map { index in
OutboundGroupItem(tag: "client\(index)", type: "Shadowsocks", urlTestTime: .now, urlTestDelay: UInt16(100 + index * 10))
}),
]
isLoading = false
} else {
connectTask?.cancel()
connectTask = Task.detached {
await connect()
}
}
}
+20 -8
View File
@@ -27,14 +27,26 @@ public class LogClient: ObservableObject {
}
public func reconnect() {
if isConnected {
return
}
if let connectTask {
connectTask.cancel()
}
connectTask = Task.detached {
await self.connect()
if ApplicationLibrary.inPreview {
logList = [
"(packet-tunnel) log server started",
"INFO[0000] router: loaded geoip database: 250 codes",
"INFO[0000] router: loaded geosite database: 1400 codes",
"INFO[0000] router: updated default interface en0, index 11",
"inbound/tun[0]: started at utun3",
"sing-box started (1.666s)",
]
isConnected = true
} else {
if isConnected {
return
}
if let connectTask {
connectTask.cancel()
}
connectTask = Task.detached {
await self.connect()
}
}
}
+8 -4
View File
@@ -61,11 +61,15 @@ public struct LogView: View {
}
private func connectLog() {
guard let profile = extensionProfile.wrappedValue else {
return
}
if profile.status.isConnected, !logClient.isConnected {
if ApplicationLibrary.inPreview {
logClient.reconnect()
} else {
guard let profile = extensionProfile.wrappedValue else {
return
}
if profile.status.isConnected, !logClient.isConnected {
logClient.reconnect()
}
}
}
}
@@ -70,15 +70,12 @@ public extension NavigationPage {
}
func visible(_ profile: ExtensionProfile?) -> Bool {
if ApplicationLibrary.inPreview {
return true
}
switch self {
case .groups:
return profile?.status.isConnectedStrict == true
case .profiles, .settings:
#if os(iOS)
return profile?.status.isConnected != true
#else
fallthrough
#endif
default:
return true
}
@@ -165,6 +165,7 @@ public struct NewProfileView: View {
var savePath = ""
var remoteURL: String? = nil
var lastUpdated: Date? = nil
if profileType == .local {
let profileConfigDirectory = FilePath.sharedDirectory.appendingPathComponent("configs", isDirectory: true)
@@ -217,6 +218,7 @@ public struct NewProfileView: View {
try remoteContent.write(to: profileConfig, atomically: true, encoding: .utf8)
savePath = profileConfig.relativePath
remoteURL = remotePath
lastUpdated = .now
}
try ProfileManager.create(Profile(name: profileName, type: profileType, path: savePath, remoteURL: remoteURL))
}
@@ -169,12 +169,19 @@ public struct ProfileView: View {
defer {
isLoading = false
}
do {
profileList = try ProfileManager.list()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
return
if ApplicationLibrary.inPreview {
profileList = [
Profile(id: 0, name: "profile local", type: .local, path: ""),
Profile(id: 1, name: "profile remote", type: .remote, path: "", lastUpdated: Date(timeIntervalSince1970: 0)),
]
} else {
do {
profileList = try ProfileManager.list()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
return
}
}
}
@@ -148,10 +148,16 @@ public struct SettingView: View {
#endif
disableMemoryLimit = SharedPreferences.disableMemoryLimit
version = LibboxVersion()
dataSize = "Loading..."
isLoading = false
dataSize = (try? FilePath.workingDirectory.formattedSize()) ?? "Unknown"
taiwanFlagAvailable = !DeviceCensorship.isChinaDevice()
if ApplicationLibrary.inPreview {
dataSize = LibboxFormatBytes(1024 * 1024 * 10)
taiwanFlagAvailable = true
isLoading = false
} else {
dataSize = "Loading..."
taiwanFlagAvailable = !DeviceCensorship.isChinaDevice()
isLoading = false
dataSize = (try? FilePath.workingDirectory.formattedSize()) ?? "Unknown"
}
}
private func clearWorkingDirectory() {
+2 -5
View File
@@ -15,19 +15,16 @@ public class Profile: Record, Identifiable, ObservableObject {
@Published public var autoUpdate: Bool
public var lastUpdated: Date?
public init(id: Int64? = nil, name: String, order: UInt32 = 0, type: ProfileType, path: String, remoteURL: String? = nil) {
public init(id: Int64? = nil, name: String, order: UInt32 = 0, type: ProfileType, path: String, remoteURL: String? = nil, lastUpdated: Date? = nil) {
self.id = id
self.name = name
self.order = order
self.type = type
self.path = path
self.remoteURL = remoteURL
self.lastUpdated = lastUpdated
autoUpdate = false
lastUpdated = nil
if type == .remote {
lastUpdated = Date()
}
super.init()
}
+4
View File
@@ -54,12 +54,16 @@ struct MainView: View {
.environment(\.selection, $selection)
.environment(\.extensionProfile, $extensionProfile)
.environment(\.logClient, $logClient)
.preferredColorScheme(.dark)
}
private func loadProfile() async {
defer {
profileLoading = false
}
if ApplicationLibrary.inPreview {
return
}
if let newProfile = try? await ExtensionProfile.load() {
if extensionProfile == nil || extensionProfile?.status == .invalid {
newProfile.register()