From 251f441a259390bb8e0074eefc4a85a5079a4d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 20 Jul 2023 13:15:13 +0800 Subject: [PATCH] Add preview support --- ApplicationLibrary/ApplicationLibrary.swift | 1 + .../Views/Dashboard/ActiveDashboardView.swift | 48 +++++++++++-------- .../Views/Dashboard/DashboardView.swift | 4 +- .../Views/Dashboard/ExtensionStatusView.swift | 11 ++++- .../Views/Dashboard/StartStopButton.swift | 14 +++++- .../Views/Groups/GroupListView.swift | 21 ++++++-- ApplicationLibrary/Views/Log/LogClient.swift | 28 +++++++---- ApplicationLibrary/Views/Log/LogView.swift | 12 +++-- ApplicationLibrary/Views/NavigationPage.swift | 9 ++-- .../Views/Profile/NewProfileView.swift | 2 + .../Views/Profile/ProfileView.swift | 19 +++++--- .../Views/Setting/SettingView.swift | 14 ++++-- Library/Database/Profile.swift | 7 +-- SFI/MainView.swift | 4 ++ 14 files changed, 135 insertions(+), 59 deletions(-) diff --git a/ApplicationLibrary/ApplicationLibrary.swift b/ApplicationLibrary/ApplicationLibrary.swift index 3a001dd..b8246da 100644 --- a/ApplicationLibrary/ApplicationLibrary.swift +++ b/ApplicationLibrary/ApplicationLibrary.swift @@ -2,4 +2,5 @@ import Foundation public class ApplicationLibrary { public static let bundle = Bundle(for: ApplicationLibrary.self) + public static let inPreview = false } diff --git a/ApplicationLibrary/Views/Dashboard/ActiveDashboardView.swift b/ApplicationLibrary/Views/Dashboard/ActiveDashboardView.swift index 808b498..3b688cb 100644 --- a/ApplicationLibrary/Views/Dashboard/ActiveDashboardView.swift +++ b/ApplicationLibrary/Views/Dashboard/ActiveDashboardView.swift @@ -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 + } } } diff --git a/ApplicationLibrary/Views/Dashboard/DashboardView.swift b/ApplicationLibrary/Views/Dashboard/DashboardView.swift index 8d6fc0b..bb09eb1 100644 --- a/ApplicationLibrary/Views/Dashboard/DashboardView.swift +++ b/ApplicationLibrary/Views/Dashboard/DashboardView.swift @@ -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 { diff --git a/ApplicationLibrary/Views/Dashboard/ExtensionStatusView.swift b/ApplicationLibrary/Views/Dashboard/ExtensionStatusView.swift index d1cca88..413ac79 100644 --- a/ApplicationLibrary/Views/Dashboard/ExtensionStatusView.swift +++ b/ApplicationLibrary/Views/Dashboard/ExtensionStatusView.swift @@ -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 { diff --git a/ApplicationLibrary/Views/Dashboard/StartStopButton.swift b/ApplicationLibrary/Views/Dashboard/StartStopButton.swift index bf510ba..978d3a1 100644 --- a/ApplicationLibrary/Views/Dashboard/StartStopButton.swift +++ b/ApplicationLibrary/Views/Dashboard/StartStopButton.swift @@ -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") }) diff --git a/ApplicationLibrary/Views/Groups/GroupListView.swift b/ApplicationLibrary/Views/Groups/GroupListView.swift index 48fa28d..ffe161b 100644 --- a/ApplicationLibrary/Views/Groups/GroupListView.swift +++ b/ApplicationLibrary/Views/Groups/GroupListView.swift @@ -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() + } } } diff --git a/ApplicationLibrary/Views/Log/LogClient.swift b/ApplicationLibrary/Views/Log/LogClient.swift index b130b9c..95d34a9 100644 --- a/ApplicationLibrary/Views/Log/LogClient.swift +++ b/ApplicationLibrary/Views/Log/LogClient.swift @@ -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() + } } } diff --git a/ApplicationLibrary/Views/Log/LogView.swift b/ApplicationLibrary/Views/Log/LogView.swift index bcedf0a..227c05f 100644 --- a/ApplicationLibrary/Views/Log/LogView.swift +++ b/ApplicationLibrary/Views/Log/LogView.swift @@ -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() + } } } } diff --git a/ApplicationLibrary/Views/NavigationPage.swift b/ApplicationLibrary/Views/NavigationPage.swift index 9d07eee..89af19b 100644 --- a/ApplicationLibrary/Views/NavigationPage.swift +++ b/ApplicationLibrary/Views/NavigationPage.swift @@ -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 } diff --git a/ApplicationLibrary/Views/Profile/NewProfileView.swift b/ApplicationLibrary/Views/Profile/NewProfileView.swift index 2d0da38..24ca118 100644 --- a/ApplicationLibrary/Views/Profile/NewProfileView.swift +++ b/ApplicationLibrary/Views/Profile/NewProfileView.swift @@ -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)) } diff --git a/ApplicationLibrary/Views/Profile/ProfileView.swift b/ApplicationLibrary/Views/Profile/ProfileView.swift index 3a7afd3..bf9eb86 100644 --- a/ApplicationLibrary/Views/Profile/ProfileView.swift +++ b/ApplicationLibrary/Views/Profile/ProfileView.swift @@ -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 + } } } diff --git a/ApplicationLibrary/Views/Setting/SettingView.swift b/ApplicationLibrary/Views/Setting/SettingView.swift index 256d48c..1a5aa72 100644 --- a/ApplicationLibrary/Views/Setting/SettingView.swift +++ b/ApplicationLibrary/Views/Setting/SettingView.swift @@ -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() { diff --git a/Library/Database/Profile.swift b/Library/Database/Profile.swift index 971148a..e1c587b 100644 --- a/Library/Database/Profile.swift +++ b/Library/Database/Profile.swift @@ -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() } diff --git a/SFI/MainView.swift b/SFI/MainView.swift index 12a63dc..228b9d3 100644 --- a/SFI/MainView.swift +++ b/SFI/MainView.swift @@ -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()