Add profile sharing

This commit is contained in:
世界
2023-07-30 14:13:57 +08:00
parent 27ffee06e2
commit 2ca6c515b9
25 changed files with 675 additions and 123 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ struct ContentView: View {
.tag(page)
.tabItem { page.label }
}
}.onChange(of: extensionProfile.status) { _ in
}.onChangeCompat(of: extensionProfile.status) {
if !selection.wrappedValue.visible(extensionProfile) {
selection.wrappedValue = NavigationPage.dashboard
}
+81 -10
View File
@@ -2,16 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSApplicationServices</key>
<dict>
<key>Advertises</key>
<array>
<dict>
<key>NSApplicationServiceIdentifier</key>
<string>sing-box:profile</string>
</dict>
</array>
</dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>io.nekohasekai.sfa.update_profiles</string>
@@ -33,6 +23,16 @@
</array>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>NSApplicationServices</key>
<dict>
<key>Advertises</key>
<array>
<dict>
<key>NSApplicationServiceIdentifier</key>
<string>sing-box:profile</string>
</dict>
</array>
</dict>
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.io.nekohasekai.sfa</key>
@@ -49,5 +49,76 @@
<array>
<string>fetch</string>
</array>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>sing-box Profile</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>io.nekohasekai.sfa.profile</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>AppIcon.icns</string>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.item</string>
<string>public.content</string>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>sing-box Profile</string>
<key>UTTypeIconFiles</key>
<array/>
<key>UTTypeIdentifier</key>
<string>io.nekohasekai.sfa.profile</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>bpf</string>
</array>
</dict>
</dict>
</array>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.item</string>
<string>public.content</string>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>sing-box Profile</string>
<key>UTTypeIconFiles</key>
<array>
<string>AppIcon.icns</string>
</array>
<key>UTTypeIdentifier</key>
<string>io.nekohasekai.sfa.profile</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>bpf</string>
</array>
</dict>
</dict>
</array>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>UISupportsDocumentBrowser</key>
<false/>
</dict>
</plist>
+22 -3
View File
@@ -10,7 +10,9 @@ struct MainView: View {
@State private var extensionProfile: ExtensionProfile?
@State private var profileLoading = true
@State private var logClient: LogClient!
@State private var importProfile: LibboxProfileContent?
@State private var importRemoteProfile: LibboxImportRemoteProfile?
@State private var alert: Alert?
var body: some View {
viewBuilder {
@@ -25,16 +27,18 @@ struct MainView: View {
ContentView()
}
}
.onChange(of: scenePhase, perform: { newValue in
.alertBinding($alert)
.onChangeCompat(of: scenePhase) { newValue in
if newValue == .active {
Task.detached {
await loadProfile()
}
}
})
}
.environment(\.selection, $selection)
.environment(\.extensionProfile, $extensionProfile)
.environment(\.logClient, $logClient)
.environment(\.importProfile, $importProfile)
.environment(\.importRemoteProfile, $importRemoteProfile)
.handlesExternalEvents(preferring: [], allowing: ["*"])
.onOpenURL(perform: openURL)
@@ -44,12 +48,27 @@ struct MainView: View {
if url.host == "import-remote-profile" {
var error: NSError?
importRemoteProfile = LibboxParseRemoteProfileImportLink(url.absoluteString, &error)
if error != nil {
if let error {
alert = Alert(error)
return
}
if selection != .profiles {
selection = .profiles
}
} else if url.pathExtension == "bpf" {
do {
_ = url.startAccessingSecurityScopedResource()
importProfile = try .from(Data(contentsOf: url))
url.stopAccessingSecurityScopedResource()
} catch {
alert = Alert(error)
return
}
if selection != .profiles {
selection = .profiles
}
} else {
alert = Alert(errorMessage: "Handled unknown URL \(url.absoluteString)")
}
}