UI fixes
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
public extension Binding {
|
||||
func withSetter(_ setter: @escaping (Value) -> Void) -> Binding<Value> {
|
||||
Binding {
|
||||
wrappedValue
|
||||
} set: { [setter] newValue, _ in
|
||||
setter(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,11 @@ import SwiftUI
|
||||
public struct ActiveDashboardView: View {
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
@Environment(\.selection) private var parentSelection
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@EnvironmentObject private var profile: ExtensionProfile
|
||||
@State private var isLoading = true
|
||||
@State private var profileList: [Profile] = []
|
||||
@State private var selectedProfileID: Int64!
|
||||
@State private var profileList: [ProfilePreview] = []
|
||||
@State private var selectedProfileID: Int64 = 0
|
||||
@State private var alert: Alert?
|
||||
@State private var selection = DashboardPage.overview
|
||||
@State private var systemProxyAvailable = false
|
||||
@@ -73,22 +74,19 @@ public struct ActiveDashboardView: View {
|
||||
OverviewView($profileList, $selectedProfileID, $systemProxyAvailable, $systemProxyEnabled)
|
||||
#endif
|
||||
}
|
||||
#if os(iOS) || os(tvOS)
|
||||
.onChangeCompat(of: scenePhase) { newValue in
|
||||
if newValue == .active {
|
||||
Task {
|
||||
await doReload()
|
||||
.onReceive(environments.profileUpdate) { _ in
|
||||
Task {
|
||||
await doReload()
|
||||
}
|
||||
}
|
||||
.onReceive(environments.selectedProfileUpdate) { _ in
|
||||
Task {
|
||||
selectedProfileID = await SharedPreferences.selectedProfileID.get()
|
||||
if profile.status.isConnected {
|
||||
await doReloadSystemProxy()
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChangeCompat(of: parentSelection.wrappedValue) { newValue in
|
||||
if newValue == .dashboard {
|
||||
Task {
|
||||
await doReload()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
.alertBinding($alert)
|
||||
}
|
||||
|
||||
@@ -98,8 +96,8 @@ public struct ActiveDashboardView: View {
|
||||
}
|
||||
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)),
|
||||
ProfilePreview(Profile(id: 0, name: "profile local", type: .local, path: "")),
|
||||
ProfilePreview(Profile(id: 1, name: "profile remote", type: .remote, path: "", lastUpdated: Date(timeIntervalSince1970: 0))),
|
||||
]
|
||||
systemProxyAvailable = true
|
||||
systemProxyEnabled = true
|
||||
@@ -107,7 +105,7 @@ public struct ActiveDashboardView: View {
|
||||
|
||||
} else {
|
||||
do {
|
||||
profileList = try await ProfileManager.list()
|
||||
profileList = try await ProfileManager.list().map { ProfilePreview($0) }
|
||||
if profileList.isEmpty {
|
||||
return
|
||||
}
|
||||
@@ -116,7 +114,7 @@ public struct ActiveDashboardView: View {
|
||||
profile.id == selectedProfileID
|
||||
})
|
||||
.isEmpty {
|
||||
selectedProfileID = profileList[0].id!
|
||||
selectedProfileID = profileList[0].id
|
||||
await SharedPreferences.selectedProfileID.set(selectedProfileID)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ public extension DashboardPage {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func contentView(_ profileList: Binding<[Profile]>, _ selectedProfileID: Binding<Int64?>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>) -> some View {
|
||||
func contentView(_ profileList: Binding<[ProfilePreview]>, _ selectedProfileID: Binding<Int64>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>) -> some View {
|
||||
viewBuilder {
|
||||
switch self {
|
||||
case .overview:
|
||||
|
||||
@@ -5,19 +5,26 @@ import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public struct OverviewView: View {
|
||||
public static let NotificationUpdateSelectedProfile = Notification.Name("update-selected-profile")
|
||||
|
||||
@Environment(\.selection) private var selection
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@EnvironmentObject private var profile: ExtensionProfile
|
||||
@Binding private var profileList: [Profile]
|
||||
@Binding private var selectedProfileID: Int64!
|
||||
@Binding private var profileList: [ProfilePreview]
|
||||
@Binding private var selectedProfileID: Int64
|
||||
@Binding private var systemProxyAvailable: Bool
|
||||
@Binding private var systemProxyEnabled: Bool
|
||||
@State private var alert: Alert?
|
||||
@State private var reasserting = false
|
||||
@State private var observer: Any?
|
||||
|
||||
public init(_ profileList: Binding<[Profile]>, _ selectedProfileID: Binding<Int64?>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>) {
|
||||
private var selectedProfileIDLocal: Binding<Int64> {
|
||||
$selectedProfileID.withSetter { newValue in
|
||||
reasserting = true
|
||||
Task { [self] in
|
||||
await switchProfile(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public init(_ profileList: Binding<[ProfilePreview]>, _ selectedProfileID: Binding<Int64>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>) {
|
||||
_profileList = profileList
|
||||
_selectedProfileID = selectedProfileID
|
||||
_systemProxyAvailable = systemProxyAvailable
|
||||
@@ -45,7 +52,7 @@ public struct OverviewView: View {
|
||||
}
|
||||
}
|
||||
Section("Profile") {
|
||||
Picker(selection: $selectedProfileID) {
|
||||
Picker(selection: selectedProfileIDLocal) {
|
||||
ForEach(profileList, id: \.id) { profile in
|
||||
Text(profile.name).tag(profile.id)
|
||||
}
|
||||
@@ -63,7 +70,7 @@ public struct OverviewView: View {
|
||||
}
|
||||
Section("Profile") {
|
||||
ForEach(profileList, id: \.id) { profile in
|
||||
Picker(profile.name, selection: $selectedProfileID) {
|
||||
Picker(profile.name, selection: selectedProfileIDLocal) {
|
||||
Text("").tag(profile.id)
|
||||
}
|
||||
}
|
||||
@@ -74,44 +81,24 @@ public struct OverviewView: View {
|
||||
}
|
||||
}
|
||||
.alertBinding($alert)
|
||||
.onChangeCompat(of: selectedProfileID) {
|
||||
reasserting = true
|
||||
Task {
|
||||
await switchProfile(selectedProfileID!)
|
||||
}
|
||||
}
|
||||
.disabled(!ApplicationLibrary.inPreview && (!profile.status.isSwitchable || reasserting))
|
||||
#if os(macOS)
|
||||
.onAppear {
|
||||
if observer == nil {
|
||||
observer = NotificationCenter.default.addObserver(forName: OverviewView.NotificationUpdateSelectedProfile, object: nil, queue: nil, using: { newProfileID in
|
||||
selectedProfileID = newProfileID.object as! Int64
|
||||
})
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
if let observer {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private nonisolated func switchProfile(_ newProfileID: Int64) async {
|
||||
private func switchProfile(_ newProfileID: Int64) async {
|
||||
await SharedPreferences.selectedProfileID.set(newProfileID)
|
||||
NotificationCenter.default.post(name: OverviewView.NotificationUpdateSelectedProfile, object: newProfileID)
|
||||
if await profile.status.isConnected {
|
||||
environments.selectedProfileUpdate.send()
|
||||
if profile.status.isConnected {
|
||||
do {
|
||||
try LibboxNewStandaloneCommandClient()!.serviceReload()
|
||||
try await serviceReload()
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = Alert(error)
|
||||
}
|
||||
alert = Alert(error)
|
||||
}
|
||||
}
|
||||
await MainActor.run {
|
||||
reasserting = false
|
||||
}
|
||||
reasserting = false
|
||||
}
|
||||
|
||||
private nonisolated func serviceReload() async throws {
|
||||
try LibboxNewStandaloneCommandClient()?.serviceReload()
|
||||
}
|
||||
|
||||
private nonisolated func setSystemProxyEnabled(_ isEnabled: Bool) async {
|
||||
|
||||
@@ -5,53 +5,30 @@ public struct LogView: View {
|
||||
@Environment(\.selection) private var selection
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
|
||||
private let logFont = Font.system(.caption2, design: .monospaced)
|
||||
|
||||
public init() {}
|
||||
|
||||
public var body: some View {
|
||||
if ApplicationLibrary.inPreview {
|
||||
let 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)",
|
||||
]
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(Array(logList.enumerated()), id: \.offset) { it in
|
||||
Text(it.element)
|
||||
.font(logFont)
|
||||
#if os(tvOS)
|
||||
.focusable()
|
||||
#endif
|
||||
Spacer(minLength: 8)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
.padding()
|
||||
}
|
||||
#if os(tvOS)
|
||||
.focusEffectDisabled()
|
||||
.focusSection()
|
||||
#endif
|
||||
} else if environments.logClient.logList.isEmpty {
|
||||
VStack {
|
||||
if environments.logClient.isConnected {
|
||||
Text("Empty logs")
|
||||
} else {
|
||||
Text("Service not started").onAppear {
|
||||
environments.connectLog()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ScrollViewReader { reader in
|
||||
LogView0().environmentObject(environments.logClient)
|
||||
}
|
||||
|
||||
private struct LogView0: View {
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@EnvironmentObject private var logClient: CommandClient
|
||||
private let logFont = Font.system(.caption2, design: .monospaced)
|
||||
|
||||
var body: some View {
|
||||
if ApplicationLibrary.inPreview {
|
||||
let 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)",
|
||||
]
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(Array(environments.logClient.logList.enumerated()), id: \.offset) { it in
|
||||
ForEach(Array(logList.enumerated()), id: \.offset) { it in
|
||||
Text(it.element)
|
||||
.font(logFont)
|
||||
#if os(tvOS)
|
||||
@@ -59,12 +36,6 @@ public struct LogView: View {
|
||||
#endif
|
||||
Spacer(minLength: 8)
|
||||
}
|
||||
|
||||
.onChangeCompat(of: environments.logClient.logList.count) { newCount in
|
||||
withAnimation {
|
||||
reader.scrollTo(newCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
.padding()
|
||||
@@ -73,8 +44,45 @@ public struct LogView: View {
|
||||
.focusEffectDisabled()
|
||||
.focusSection()
|
||||
#endif
|
||||
.onAppear {
|
||||
reader.scrollTo(environments.logClient.logList.count - 1)
|
||||
} else if logClient.logList.isEmpty {
|
||||
VStack {
|
||||
if logClient.isConnected {
|
||||
Text("Empty logs")
|
||||
} else {
|
||||
Text("Service not started").onAppear {
|
||||
environments.connectLog()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ScrollViewReader { reader in
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(Array(logClient.logList.enumerated()), id: \.offset) { it in
|
||||
Text(it.element)
|
||||
.font(logFont)
|
||||
#if os(tvOS)
|
||||
.focusable()
|
||||
#endif
|
||||
Spacer(minLength: 8)
|
||||
}
|
||||
|
||||
.onChangeCompat(of: logClient.logList.count) { newCount in
|
||||
withAnimation {
|
||||
reader.scrollTo(newCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
.padding()
|
||||
}
|
||||
#if os(tvOS)
|
||||
.focusEffectDisabled()
|
||||
.focusSection()
|
||||
#endif
|
||||
.onAppear {
|
||||
reader.scrollTo(logClient.logList.count - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,15 @@ public struct EditProfileView: View {
|
||||
@Environment(\.openWindow) private var openWindow
|
||||
#endif
|
||||
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@EnvironmentObject private var profile: Profile
|
||||
|
||||
@State private var isLoading = false
|
||||
@State private var isChanged = false
|
||||
@State private var alert: Alert?
|
||||
private let updateCallback: (() -> Void)?
|
||||
public init(_ updateCallback: (() -> Void)? = nil) {
|
||||
self.updateCallback = updateCallback
|
||||
}
|
||||
|
||||
public init() {}
|
||||
public var body: some View {
|
||||
FormView {
|
||||
FormItem("Name") {
|
||||
@@ -170,7 +168,7 @@ public struct EditProfileView: View {
|
||||
do {
|
||||
try await Task.sleep(nanoseconds: UInt64(100 * Double(NSEC_PER_MSEC)))
|
||||
try await profile.updateRemoteProfile()
|
||||
await performCallback()
|
||||
environments.profileUpdate.send()
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
}
|
||||
@@ -183,7 +181,7 @@ public struct EditProfileView: View {
|
||||
alert = Alert(error)
|
||||
return
|
||||
}
|
||||
await performCallback()
|
||||
environments.profileUpdate.send()
|
||||
dismiss()
|
||||
}
|
||||
|
||||
@@ -201,14 +199,6 @@ public struct EditProfileView: View {
|
||||
}
|
||||
isChanged = false
|
||||
isLoading = false
|
||||
await performCallback()
|
||||
}
|
||||
|
||||
private func performCallback() async {
|
||||
if let updateCallback {
|
||||
updateCallback()
|
||||
} else {
|
||||
NotificationCenter.default.post(name: ProfileView.notificationName, object: nil)
|
||||
}
|
||||
environments.profileUpdate.send()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ public struct NewProfileView: View {
|
||||
public static let windowID = "new-profile"
|
||||
#endif
|
||||
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
@State private var isSaving = false
|
||||
@@ -27,9 +28,7 @@ public struct NewProfileView: View {
|
||||
public let url: String
|
||||
}
|
||||
|
||||
private let callback: (() async -> Void)?
|
||||
public init(_ importRequest: ImportRequest? = nil, _ callback: (() async -> Void)? = nil) {
|
||||
self.callback = callback
|
||||
public init(_ importRequest: ImportRequest? = nil) {
|
||||
if let importRequest {
|
||||
_profileName = .init(initialValue: importRequest.name)
|
||||
_profileType = .init(initialValue: .remote)
|
||||
@@ -156,12 +155,9 @@ public struct NewProfileView: View {
|
||||
alert = Alert(error)
|
||||
return
|
||||
}
|
||||
if let callback {
|
||||
await callback()
|
||||
}
|
||||
environments.profileUpdate.send()
|
||||
dismiss()
|
||||
#if os(macOS)
|
||||
NotificationCenter.default.post(name: ProfileView.notificationName, object: nil)
|
||||
resetFields()
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@ import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public struct ProfileView: View {
|
||||
public static let notificationName = Notification.Name("\(FilePath.packageName).update-profile")
|
||||
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@Environment(\.importProfile) private var importProfile
|
||||
@Environment(\.importRemoteProfile) private var importRemoteProfile
|
||||
@State private var importRemoteProfileRequest: NewProfileView.ImportRequest?
|
||||
@@ -17,7 +16,7 @@ public struct ProfileView: View {
|
||||
@State private var isUpdating = false
|
||||
|
||||
@State private var alert: Alert?
|
||||
@State private var profileList: [Profile] = []
|
||||
@State private var profileList: [ProfilePreview] = []
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
@State private var editMode = EditMode.inactive
|
||||
@@ -29,10 +28,7 @@ public struct ProfileView: View {
|
||||
@Environment(\.devicePickerSupports) private var devicePickerSupports
|
||||
#endif
|
||||
|
||||
@State private var observer: Any?
|
||||
|
||||
public init() {}
|
||||
|
||||
public var body: some View {
|
||||
VStack {
|
||||
if isLoading {
|
||||
@@ -46,17 +42,13 @@ public struct ProfileView: View {
|
||||
ZStack {
|
||||
if let importRemoteProfileRequest {
|
||||
NavigationDestinationCompat(isPresented: $importRemoteProfilePresented) {
|
||||
NewProfileView(importRemoteProfileRequest) {
|
||||
await doReload()
|
||||
}
|
||||
NewProfileView(importRemoteProfileRequest)
|
||||
}
|
||||
}
|
||||
FormView {
|
||||
#if os(iOS)
|
||||
NavigationLink {
|
||||
NewProfileView {
|
||||
await doReload()
|
||||
}
|
||||
NewProfileView()
|
||||
} label: {
|
||||
Text("New Profile").foregroundColor(.accentColor)
|
||||
}
|
||||
@@ -64,9 +56,7 @@ public struct ProfileView: View {
|
||||
#elseif os(tvOS)
|
||||
Section {
|
||||
NavigationLink {
|
||||
NewProfileView {
|
||||
await doReload()
|
||||
}
|
||||
NewProfileView()
|
||||
} label: {
|
||||
Text("New Profile").foregroundColor(.accentColor)
|
||||
}
|
||||
@@ -85,7 +75,7 @@ public struct ProfileView: View {
|
||||
Text("Empty profiles")
|
||||
} else {
|
||||
List {
|
||||
ForEach(profileList, id: \.mustID) { profile in
|
||||
ForEach(profileList, id: \.id) { profile in
|
||||
viewBuilder {
|
||||
if editMode.isEditing == true {
|
||||
Text(profile.name)
|
||||
@@ -106,7 +96,7 @@ public struct ProfileView: View {
|
||||
} else {
|
||||
FormView {
|
||||
List {
|
||||
ForEach(profileList, id: \.mustID) { profile in
|
||||
ForEach(profileList, id: \.id) { profile in
|
||||
ProfileItem(self, profile)
|
||||
}
|
||||
.onMove(perform: moveProfile)
|
||||
@@ -128,15 +118,6 @@ public struct ProfileView: View {
|
||||
importRemoteProfile.wrappedValue = nil
|
||||
createImportRemoteProfileDialog(remoteProfile)
|
||||
}
|
||||
#if os(macOS)
|
||||
if observer == nil {
|
||||
observer = NotificationCenter.default.addObserver(forName: ProfileView.notificationName, object: nil, queue: .main) { _ in
|
||||
Task {
|
||||
await doReload()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.onChangeCompat(of: importProfile.wrappedValue) { newValue in
|
||||
if let newValue {
|
||||
@@ -150,23 +131,14 @@ public struct ProfileView: View {
|
||||
createImportRemoteProfileDialog(newValue)
|
||||
}
|
||||
}
|
||||
#if os(macOS)
|
||||
.onDisappear {
|
||||
if let observer {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
observer = nil
|
||||
.onReceive(environments.profileUpdate) { _ in
|
||||
profileList = []
|
||||
isLoading = true
|
||||
// not updated, but why?
|
||||
// Task {
|
||||
// await doReload()
|
||||
// }
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem {
|
||||
Button {
|
||||
openWindow(id: NewProfileView.windowID)
|
||||
} label: {
|
||||
Label("New Profile", systemImage: "plus.square.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if os(iOS)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
@@ -232,18 +204,15 @@ public struct ProfileView: View {
|
||||
private func doReload() async {
|
||||
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)),
|
||||
ProfilePreview(Profile(id: 0, name: "profile local", type: .local, path: "")),
|
||||
ProfilePreview(Profile(id: 1, name: "profile remote", type: .remote, path: "", lastUpdated: Date(timeIntervalSince1970: 0))),
|
||||
]
|
||||
} else {
|
||||
defer {
|
||||
isLoading = false
|
||||
}
|
||||
do {
|
||||
if !profileList.isEmpty {
|
||||
profileList.removeAll()
|
||||
}
|
||||
profileList = try await ProfileManager.list()
|
||||
profileList = try await ProfileManager.list().map { ProfilePreview($0) }
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
return
|
||||
@@ -279,11 +248,12 @@ public struct ProfileView: View {
|
||||
private func moveProfile(from source: IndexSet, to destination: Int) {
|
||||
profileList.move(fromOffsets: source, toOffset: destination)
|
||||
for (index, profile) in profileList.enumerated() {
|
||||
profile.order = UInt32(index)
|
||||
profileList[index].order = UInt32(index)
|
||||
profile.origin.order = UInt32(index)
|
||||
}
|
||||
Task {
|
||||
do {
|
||||
try await ProfileManager.update(profileList)
|
||||
try await ProfileManager.update(profileList.map(\.origin))
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
}
|
||||
@@ -292,7 +262,7 @@ public struct ProfileView: View {
|
||||
|
||||
private func deleteProfile(where profileIndex: IndexSet) {
|
||||
let profileToDelete = profileIndex.map { index in
|
||||
profileList[index]
|
||||
profileList[index].origin
|
||||
}
|
||||
profileList.remove(atOffsets: profileIndex)
|
||||
Task {
|
||||
@@ -306,16 +276,16 @@ public struct ProfileView: View {
|
||||
|
||||
public struct ProfileItem: View {
|
||||
private let parent: ProfileView
|
||||
private let profile: Profile
|
||||
public init(_ parent: ProfileView, _ profile: Profile) {
|
||||
@State private var profile: ProfilePreview
|
||||
public init(_ parent: ProfileView, _ profile: ProfilePreview) {
|
||||
self.parent = parent
|
||||
self.profile = profile
|
||||
_profile = State(initialValue: profile)
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
#if os(iOS) || os(macOS)
|
||||
if #available(iOS 16.0, macOS 13.0,*) {
|
||||
body0.draggable(profile)
|
||||
body0.draggable(profile.origin)
|
||||
} else {
|
||||
body0
|
||||
}
|
||||
@@ -329,23 +299,20 @@ public struct ProfileView: View {
|
||||
viewBuilder {
|
||||
#if !os(macOS)
|
||||
NavigationLink {
|
||||
EditProfileView {
|
||||
Task {
|
||||
await parent.doReload()
|
||||
}
|
||||
}.environmentObject(profile)
|
||||
EditProfileView().environmentObject(profile.origin)
|
||||
} label: {
|
||||
Text(profile.name)
|
||||
}
|
||||
.contextMenu {
|
||||
ProfileShareButton(parent.$alert, profile) {
|
||||
ProfileShareButton(parent.$alert, profile.origin) {
|
||||
Label("Share", systemImage: "square.and.arrow.up.fill")
|
||||
}
|
||||
if profile.type == .remote {
|
||||
Button {
|
||||
parent.isUpdating = true
|
||||
Task {
|
||||
await parent.updateProfile(profile)
|
||||
await parent.updateProfile(profile.origin)
|
||||
profile = ProfilePreview(profile.origin)
|
||||
}
|
||||
} label: {
|
||||
Label("Update", systemImage: "arrow.clockwise")
|
||||
@@ -353,7 +320,7 @@ public struct ProfileView: View {
|
||||
}
|
||||
Button(role: .destructive) {
|
||||
Task {
|
||||
await parent.deleteProfile(profile)
|
||||
await parent.deleteProfile(profile.origin)
|
||||
}
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash.fill")
|
||||
@@ -365,7 +332,7 @@ public struct ProfileView: View {
|
||||
Text(profile.name)
|
||||
if profile.type == .remote {
|
||||
Spacer(minLength: 4)
|
||||
Text("Last Updated: \(profile.lastUpdatedString)").font(.caption)
|
||||
Text("Last Updated: \(profile.origin.lastUpdatedString)").font(.caption)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
@@ -373,23 +340,24 @@ public struct ProfileView: View {
|
||||
Button {
|
||||
parent.isUpdating = true
|
||||
Task {
|
||||
await parent.updateProfile(profile)
|
||||
await parent.updateProfile(profile.origin)
|
||||
profile = ProfilePreview(profile.origin)
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
}
|
||||
}
|
||||
ProfileShareButton(parent.$alert, profile) {
|
||||
ProfileShareButton(parent.$alert, profile.origin) {
|
||||
Image(systemName: "square.and.arrow.up.fill")
|
||||
}
|
||||
Button {
|
||||
parent.openWindow(id: EditProfileWindowView.windowID, value: profile.mustID)
|
||||
parent.openWindow(id: EditProfileWindowView.windowID, value: profile.id)
|
||||
} label: {
|
||||
Image(systemName: "pencil")
|
||||
}
|
||||
Button {
|
||||
Task {
|
||||
await parent.deleteProfile(profile)
|
||||
await parent.deleteProfile(profile.origin)
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "trash.fill")
|
||||
|
||||
@@ -69,6 +69,32 @@ public class Profile: Record, Identifiable, ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
public struct ProfilePreview: Identifiable, Hashable {
|
||||
public let id: Int64
|
||||
public let name: String
|
||||
public var order: UInt32
|
||||
public let type: ProfileType
|
||||
public let path: String
|
||||
public let remoteURL: String?
|
||||
public let autoUpdate: Bool
|
||||
public let autoUpdateInterval: Int32
|
||||
public let lastUpdated: Date?
|
||||
public let origin: Profile
|
||||
|
||||
public init(_ profile: Profile) {
|
||||
id = profile.mustID
|
||||
name = profile.name
|
||||
order = profile.order
|
||||
type = profile.type
|
||||
path = profile.path
|
||||
remoteURL = profile.remoteURL
|
||||
autoUpdate = profile.autoUpdate
|
||||
autoUpdateInterval = profile.autoUpdateInterval
|
||||
lastUpdated = profile.lastUpdated
|
||||
origin = profile
|
||||
}
|
||||
}
|
||||
|
||||
public enum ProfileType: Int {
|
||||
case local = 0, icloud, remote
|
||||
}
|
||||
|
||||
@@ -93,33 +93,37 @@ public class CommandClient: ObservableObject {
|
||||
}
|
||||
|
||||
func connected() {
|
||||
DispatchQueue.main.sync {
|
||||
DispatchQueue.main.async { [self] in
|
||||
commandClient.isConnected = true
|
||||
}
|
||||
}
|
||||
|
||||
func disconnected(_: String?) {
|
||||
DispatchQueue.main.sync {
|
||||
DispatchQueue.main.async { [self] in
|
||||
commandClient.isConnected = false
|
||||
}
|
||||
}
|
||||
|
||||
func clearLog() {
|
||||
DispatchQueue.main.async { [self] in
|
||||
commandClient.logList.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
func writeLog(_ message: String?) {
|
||||
guard let message else {
|
||||
return
|
||||
}
|
||||
var logList = commandClient.logList
|
||||
if logList.count > commandClient.logMaxLines {
|
||||
logList.removeFirst()
|
||||
}
|
||||
logList.append(message)
|
||||
DispatchQueue.main.sync {
|
||||
commandClient.logList = logList
|
||||
DispatchQueue.main.async { [self] in
|
||||
if commandClient.logList.count > commandClient.logMaxLines {
|
||||
commandClient.logList.removeFirst()
|
||||
}
|
||||
commandClient.logList.append(message)
|
||||
}
|
||||
}
|
||||
|
||||
func writeStatus(_ message: LibboxStatusMessage?) {
|
||||
DispatchQueue.main.sync {
|
||||
DispatchQueue.main.async { [self] in
|
||||
commandClient.status = message
|
||||
}
|
||||
}
|
||||
@@ -132,20 +136,20 @@ public class CommandClient: ObservableObject {
|
||||
while groups.hasNext() {
|
||||
newGroups.append(groups.next()!)
|
||||
}
|
||||
DispatchQueue.main.sync {
|
||||
DispatchQueue.main.async { [self] in
|
||||
commandClient.groups = newGroups
|
||||
}
|
||||
}
|
||||
|
||||
func initializeClashMode(_ modeList: LibboxStringIteratorProtocol?, currentMode: String?) {
|
||||
DispatchQueue.main.sync {
|
||||
DispatchQueue.main.async { [self] in
|
||||
commandClient.clashModeList = modeList!.toArray()
|
||||
commandClient.clashMode = currentMode!
|
||||
}
|
||||
}
|
||||
|
||||
func updateClashMode(_ newMode: String?) {
|
||||
DispatchQueue.main.sync {
|
||||
DispatchQueue.main.async { [self] in
|
||||
commandClient.clashMode = newMode!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
public class ExtensionEnvironments: ObservableObject {
|
||||
@Published public var logClient = CommandClient(.log)
|
||||
@Published public var extensionProfileLoading = true
|
||||
@Published public var extensionProfile: ExtensionProfile?
|
||||
public let profileUpdate = ObjectWillChangePublisher()
|
||||
public let selectedProfileUpdate = ObjectWillChangePublisher()
|
||||
|
||||
public init() {}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc
|
||||
}
|
||||
|
||||
public func serviceReload() throws {
|
||||
Task {
|
||||
runBlocking { [self] in
|
||||
await tunnel.reloadService()
|
||||
}
|
||||
}
|
||||
@@ -214,4 +214,8 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc
|
||||
try await self.tunnel.setTunnelNetworkSettings(networkSettings)
|
||||
}
|
||||
}
|
||||
|
||||
func reset() {
|
||||
networkSettings = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +131,9 @@ open class ExtensionProvider: NEPacketTunnelProvider {
|
||||
boxService = nil
|
||||
commandServer.setService(nil)
|
||||
}
|
||||
if let platformInterface {
|
||||
platformInterface.reset()
|
||||
}
|
||||
}
|
||||
|
||||
func reloadService() async {
|
||||
|
||||
@@ -39,21 +39,26 @@ public struct MacApplication: Scene {
|
||||
|
||||
WindowGroup("New Profile", id: NewProfileView.windowID, for: NewProfileView.ImportRequest.self) { importRequest in
|
||||
NewProfileView(importRequest.wrappedValue)
|
||||
.environmentObject(environments)
|
||||
}.commandsRemoved()
|
||||
|
||||
WindowGroup("Edit Profile", id: EditProfileWindowView.windowID, for: Int64.self) { profileID in
|
||||
EditProfileWindowView(profileID.wrappedValue)
|
||||
.environmentObject(environments)
|
||||
}.commandsRemoved()
|
||||
|
||||
WindowGroup("Edit Content", id: EditProfileContentView.windowID, for: EditProfileContentView.Context.self) { context in
|
||||
EditProfileContentView(context.wrappedValue)
|
||||
.environmentObject(environments)
|
||||
}.commandsRemoved()
|
||||
|
||||
Window("Service Log", id: ServiceLogView.windowID) {
|
||||
ServiceLogView()
|
||||
.environmentObject(environments)
|
||||
}
|
||||
MenuBarExtra(isInserted: $showMenuBarExtra) {
|
||||
MenuView(isMenuPresented: $isMenuPresented)
|
||||
.environmentObject(environments)
|
||||
} label: {
|
||||
Image("MenuIcon")
|
||||
}
|
||||
|
||||
+22
-21
@@ -106,6 +106,7 @@ public struct MenuView: View {
|
||||
}
|
||||
|
||||
private struct ProfilePicker: View {
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@ObservedObject private var profile: ExtensionProfile
|
||||
|
||||
init(_ profile: ExtensionProfile) {
|
||||
@@ -113,12 +114,20 @@ public struct MenuView: View {
|
||||
}
|
||||
|
||||
@State private var isLoading = true
|
||||
@State private var profileList: [Profile] = []
|
||||
@State private var selectedProfileID: Int64!
|
||||
@State private var profileList: [ProfilePreview] = []
|
||||
@State private var selectedProfileID: Int64 = 0
|
||||
@State private var reasserting = false
|
||||
@State private var observer: Any?
|
||||
@State private var alert: Alert?
|
||||
|
||||
private var selectedProfileIDLocal: Binding<Int64> {
|
||||
$selectedProfileID.withSetter { newValue in
|
||||
reasserting = true
|
||||
Task { [self] in
|
||||
await switchProfile(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
viewBuilder {
|
||||
if isLoading {
|
||||
@@ -132,32 +141,24 @@ public struct MenuView: View {
|
||||
Text("Empty profiles")
|
||||
} else {
|
||||
MenuSection("Profile")
|
||||
Picker("", selection: $selectedProfileID) {
|
||||
Picker("", selection: selectedProfileIDLocal) {
|
||||
ForEach(profileList, id: \.id) { profile in
|
||||
Text(profile.name)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.inline)
|
||||
.onChangeCompat(of: selectedProfileID) {
|
||||
reasserting = true
|
||||
Task {
|
||||
await switchProfile(selectedProfileID!)
|
||||
}
|
||||
}
|
||||
.disabled(!profile.status.isSwitchable || reasserting)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if observer == nil {
|
||||
observer = NotificationCenter.default.addObserver(forName: OverviewView.NotificationUpdateSelectedProfile, object: nil, queue: nil, using: { notification in
|
||||
selectedProfileID = notification.object as! Int64
|
||||
})
|
||||
.onReceive(environments.profileUpdate) { _ in
|
||||
Task {
|
||||
await doReload()
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
if let observer {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
.onReceive(environments.selectedProfileUpdate) { _ in
|
||||
Task {
|
||||
selectedProfileID = await SharedPreferences.selectedProfileID.get()
|
||||
}
|
||||
}
|
||||
.alertBinding($alert)
|
||||
@@ -168,7 +169,7 @@ public struct MenuView: View {
|
||||
isLoading = false
|
||||
}
|
||||
do {
|
||||
profileList = try await ProfileManager.list()
|
||||
profileList = try await ProfileManager.list().map { ProfilePreview($0) }
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
return
|
||||
@@ -181,14 +182,14 @@ public struct MenuView: View {
|
||||
profile.id == selectedProfileID
|
||||
})
|
||||
.isEmpty {
|
||||
selectedProfileID = profileList[0].id!
|
||||
selectedProfileID = profileList[0].id
|
||||
await SharedPreferences.selectedProfileID.set(selectedProfileID)
|
||||
}
|
||||
}
|
||||
|
||||
private func switchProfile(_ newProfileID: Int64) async {
|
||||
await SharedPreferences.selectedProfileID.set(newProfileID)
|
||||
NotificationCenter.default.post(name: OverviewView.NotificationUpdateSelectedProfile, object: newProfileID)
|
||||
environments.selectedProfileUpdate.send()
|
||||
if profile.status.isConnected {
|
||||
do {
|
||||
try await serviceReload()
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
3AC729F02A75D9D000FE8EC1 /* Profile+Transferable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC729EF2A75D9D000FE8EC1 /* Profile+Transferable.swift */; };
|
||||
3AC729F22A76088E00FE8EC1 /* ShareButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC729F12A76088E00FE8EC1 /* ShareButton.swift */; };
|
||||
3AC8CF9B2A736C750002AF3C /* ImportProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC8CF9A2A736C750002AF3C /* ImportProfileView.swift */; };
|
||||
3ACE6DE32ACADF55009D9A8A /* Binding+Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACE6DE22ACADF55009D9A8A /* Binding+Setter.swift */; };
|
||||
3AD0953D2A70EB310052764E /* Profile+Share.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AD0953C2A70EB310052764E /* Profile+Share.swift */; };
|
||||
3ADBB4252A7389640041D44F /* ProfileServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADBB4242A7389640041D44F /* ProfileServer.swift */; };
|
||||
3ADBB42A2A73A7060041D44F /* NWSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADBB4292A73A7060041D44F /* NWSocket.swift */; };
|
||||
@@ -491,6 +492,7 @@
|
||||
3AC729EF2A75D9D000FE8EC1 /* Profile+Transferable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Transferable.swift"; sourceTree = "<group>"; };
|
||||
3AC729F12A76088E00FE8EC1 /* ShareButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareButton.swift; sourceTree = "<group>"; };
|
||||
3AC8CF9A2A736C750002AF3C /* ImportProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportProfileView.swift; sourceTree = "<group>"; };
|
||||
3ACE6DE22ACADF55009D9A8A /* Binding+Setter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Setter.swift"; sourceTree = "<group>"; };
|
||||
3AD0953C2A70EB310052764E /* Profile+Share.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Share.swift"; sourceTree = "<group>"; };
|
||||
3ADBB4242A7389640041D44F /* ProfileServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileServer.swift; sourceTree = "<group>"; };
|
||||
3ADBB4292A73A7060041D44F /* NWSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NWSocket.swift; sourceTree = "<group>"; };
|
||||
@@ -988,6 +990,7 @@
|
||||
3A99B42B2A75288C0010D4B0 /* ViewCompat.swift */,
|
||||
3A99B42D2A752ABB0010D4B0 /* NavigationDestinationCompat.swift */,
|
||||
3AC729F12A76088E00FE8EC1 /* ShareButton.swift */,
|
||||
3ACE6DE22ACADF55009D9A8A /* Binding+Setter.swift */,
|
||||
);
|
||||
path = Abstract;
|
||||
sourceTree = "<group>";
|
||||
@@ -1463,6 +1466,7 @@
|
||||
3A99B42E2A752ABB0010D4B0 /* NavigationDestinationCompat.swift in Sources */,
|
||||
3AC729F22A76088E00FE8EC1 /* ShareButton.swift in Sources */,
|
||||
3A0C6D3C2A79D46500A4DF2B /* OverviewView.swift in Sources */,
|
||||
3ACE6DE32ACADF55009D9A8A /* Binding+Setter.swift in Sources */,
|
||||
3A4EAD262A4FEB65005435B3 /* ExtensionStatusView.swift in Sources */,
|
||||
3A4EAD252A4FEB65005435B3 /* StartStopButton.swift in Sources */,
|
||||
3A4EAD2B2A4FEB6D005435B3 /* ViewBuilder.swift in Sources */,
|
||||
@@ -1962,7 +1966,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.5.1;
|
||||
MARKETING_VERSION = 1.6.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
||||
PRODUCT_NAME = "sing-box";
|
||||
SDKROOT = appletvos;
|
||||
@@ -1996,7 +2000,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.5.1;
|
||||
MARKETING_VERSION = 1.6.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
||||
PRODUCT_NAME = "sing-box";
|
||||
SDKROOT = appletvos;
|
||||
@@ -2230,7 +2234,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.5.1;
|
||||
MARKETING_VERSION = 1.6.0;
|
||||
OTHER_CODE_SIGN_FLAGS = "--deep";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
||||
PRODUCT_NAME = "sing-box";
|
||||
@@ -2270,7 +2274,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.5.1;
|
||||
MARKETING_VERSION = 1.6.0;
|
||||
OTHER_CODE_SIGN_FLAGS = "--deep";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
||||
PRODUCT_NAME = "sing-box";
|
||||
@@ -2293,7 +2297,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 85;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = Z56Z6NYZN2;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -2309,7 +2313,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.5.1;
|
||||
MARKETING_VERSION = 1.6.0;
|
||||
OTHER_CODE_SIGN_FLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
||||
PRODUCT_NAME = "sing-box";
|
||||
@@ -2331,7 +2335,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 85;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = Z56Z6NYZN2;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -2347,7 +2351,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.5.1;
|
||||
MARKETING_VERSION = 1.6.0;
|
||||
OTHER_CODE_SIGN_FLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
||||
PRODUCT_NAME = "sing-box";
|
||||
@@ -2391,7 +2395,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACH_O_TYPE = mh_dylib;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
|
||||
@@ -2445,7 +2449,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACH_O_TYPE = mh_dylib;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
|
||||
@@ -2489,7 +2493,7 @@
|
||||
"@executable_path/../../../../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.5.1;
|
||||
MARKETING_VERSION = "1.6.0-alpha.1";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system;
|
||||
PRODUCT_NAME = "$(inherited)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -2525,7 +2529,7 @@
|
||||
"@executable_path/../../../../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.5.1;
|
||||
MARKETING_VERSION = "1.6.0-alpha.1";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system;
|
||||
PRODUCT_NAME = "$(inherited)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -2565,7 +2569,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.5.1;
|
||||
MARKETING_VERSION = "1.6.0-alpha.1";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
|
||||
PRODUCT_NAME = SFM;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -2604,7 +2608,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.5.1;
|
||||
MARKETING_VERSION = "1.6.0-alpha.1";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
|
||||
PRODUCT_NAME = SFM;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
||||
Reference in New Issue
Block a user