Sync push
This commit is contained in:
parent
1a459672d9
commit
1c70b21e25
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
.DS_Store
|
@ -8,11 +8,11 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
881E0B26284B74E200435EC2 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881E0B25284B74E200435EC2 /* Data+Extensions.swift */; };
|
881E0B26284B74E200435EC2 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881E0B25284B74E200435EC2 /* Data+Extensions.swift */; };
|
||||||
88DBE72A284B989C00D1573B /* DeviceList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DBE729284B989C00D1573B /* DeviceList.swift */; };
|
88F92BA928C67EEB0078BEC4 /* TokenUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F92BA828C67EEB0078BEC4 /* TokenUpload.swift */; };
|
||||||
|
88F92BAC28C67F8B0078BEC4 /* BinaryCodable in Frameworks */ = {isa = PBXBuildFile; productRef = 88F92BAB28C67F8B0078BEC4 /* BinaryCodable */; };
|
||||||
E234995C284E1D02002B55F8 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = E234995B284E1D02002B55F8 /* SFSafeSymbols */; };
|
E234995C284E1D02002B55F8 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = E234995B284E1D02002B55F8 /* SFSafeSymbols */; };
|
||||||
E234995F284E372B002B55F8 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E2349961284E372B002B55F8 /* Localizable.strings */; };
|
E234995F284E372B002B55F8 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E2349961284E372B002B55F8 /* Localizable.strings */; };
|
||||||
E2349964284F3133002B55F8 /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2349963284F3133002B55F8 /* TextEntryField.swift */; };
|
E2349964284F3133002B55F8 /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2349963284F3133002B55F8 /* TextEntryField.swift */; };
|
||||||
E2349968284F78E3002B55F8 /* Push in Frameworks */ = {isa = PBXBuildFile; productRef = E2349967284F78E3002B55F8 /* Push */; };
|
|
||||||
E29A7E47284B6143000B908A /* FlurSchnapsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29A7E46284B6143000B908A /* FlurSchnapsApp.swift */; };
|
E29A7E47284B6143000B908A /* FlurSchnapsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29A7E46284B6143000B908A /* FlurSchnapsApp.swift */; };
|
||||||
E29A7E49284B6143000B908A /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29A7E48284B6143000B908A /* ContentView.swift */; };
|
E29A7E49284B6143000B908A /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29A7E48284B6143000B908A /* ContentView.swift */; };
|
||||||
E29A7E4B284B6144000B908A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E29A7E4A284B6144000B908A /* Assets.xcassets */; };
|
E29A7E4B284B6144000B908A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E29A7E4A284B6144000B908A /* Assets.xcassets */; };
|
||||||
@ -23,7 +23,7 @@
|
|||||||
881E0B25284B74E200435EC2 /* Data+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = "<group>"; };
|
881E0B25284B74E200435EC2 /* Data+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
88DBE727284B7EB200D1573B /* FlurSchnaps.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FlurSchnaps.entitlements; sourceTree = "<group>"; };
|
88DBE727284B7EB200D1573B /* FlurSchnaps.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FlurSchnaps.entitlements; sourceTree = "<group>"; };
|
||||||
88DBE728284B813500D1573B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
88DBE728284B813500D1573B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
88DBE729284B989C00D1573B /* DeviceList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceList.swift; sourceTree = "<group>"; };
|
88F92BA828C67EEB0078BEC4 /* TokenUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenUpload.swift; sourceTree = "<group>"; };
|
||||||
E2349960284E372B002B55F8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
E2349960284E372B002B55F8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
E2349962284E3733002B55F8 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
E2349962284E3733002B55F8 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
E2349963284F3133002B55F8 /* TextEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = "<group>"; };
|
E2349963284F3133002B55F8 /* TextEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = "<group>"; };
|
||||||
@ -39,7 +39,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
E2349968284F78E3002B55F8 /* Push in Frameworks */,
|
88F92BAC28C67F8B0078BEC4 /* BinaryCodable in Frameworks */,
|
||||||
E234995C284E1D02002B55F8 /* SFSafeSymbols in Frameworks */,
|
E234995C284E1D02002B55F8 /* SFSafeSymbols in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -72,10 +72,10 @@
|
|||||||
E29A7E46284B6143000B908A /* FlurSchnapsApp.swift */,
|
E29A7E46284B6143000B908A /* FlurSchnapsApp.swift */,
|
||||||
E29A7E48284B6143000B908A /* ContentView.swift */,
|
E29A7E48284B6143000B908A /* ContentView.swift */,
|
||||||
E2349963284F3133002B55F8 /* TextEntryField.swift */,
|
E2349963284F3133002B55F8 /* TextEntryField.swift */,
|
||||||
88DBE729284B989C00D1573B /* DeviceList.swift */,
|
|
||||||
881E0B25284B74E200435EC2 /* Data+Extensions.swift */,
|
881E0B25284B74E200435EC2 /* Data+Extensions.swift */,
|
||||||
E29A7E4A284B6144000B908A /* Assets.xcassets */,
|
E29A7E4A284B6144000B908A /* Assets.xcassets */,
|
||||||
E29A7E4C284B6144000B908A /* Preview Content */,
|
E29A7E4C284B6144000B908A /* Preview Content */,
|
||||||
|
88F92BA828C67EEB0078BEC4 /* TokenUpload.swift */,
|
||||||
);
|
);
|
||||||
path = FlurSchnaps;
|
path = FlurSchnaps;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -106,7 +106,7 @@
|
|||||||
name = FlurSchnaps;
|
name = FlurSchnaps;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
E234995B284E1D02002B55F8 /* SFSafeSymbols */,
|
E234995B284E1D02002B55F8 /* SFSafeSymbols */,
|
||||||
E2349967284F78E3002B55F8 /* Push */,
|
88F92BAB28C67F8B0078BEC4 /* BinaryCodable */,
|
||||||
);
|
);
|
||||||
productName = FlurSchnaps;
|
productName = FlurSchnaps;
|
||||||
productReference = E29A7E43284B6143000B908A /* FlurSchnaps.app */;
|
productReference = E29A7E43284B6143000B908A /* FlurSchnaps.app */;
|
||||||
@ -139,7 +139,7 @@
|
|||||||
mainGroup = E29A7E3A284B6143000B908A;
|
mainGroup = E29A7E3A284B6143000B908A;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
E234995A284E1D02002B55F8 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */,
|
E234995A284E1D02002B55F8 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */,
|
||||||
E2349966284F78E3002B55F8 /* XCRemoteSwiftPackageReference "Push-iOS" */,
|
88F92BAA28C67F8B0078BEC4 /* XCRemoteSwiftPackageReference "BinaryCodable" */,
|
||||||
);
|
);
|
||||||
productRefGroup = E29A7E44284B6143000B908A /* Products */;
|
productRefGroup = E29A7E44284B6143000B908A /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@ -171,8 +171,8 @@
|
|||||||
E2349964284F3133002B55F8 /* TextEntryField.swift in Sources */,
|
E2349964284F3133002B55F8 /* TextEntryField.swift in Sources */,
|
||||||
E29A7E49284B6143000B908A /* ContentView.swift in Sources */,
|
E29A7E49284B6143000B908A /* ContentView.swift in Sources */,
|
||||||
881E0B26284B74E200435EC2 /* Data+Extensions.swift in Sources */,
|
881E0B26284B74E200435EC2 /* Data+Extensions.swift in Sources */,
|
||||||
88DBE72A284B989C00D1573B /* DeviceList.swift in Sources */,
|
|
||||||
E29A7E47284B6143000B908A /* FlurSchnapsApp.swift in Sources */,
|
E29A7E47284B6143000B908A /* FlurSchnapsApp.swift in Sources */,
|
||||||
|
88F92BA928C67EEB0078BEC4 /* TokenUpload.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -395,6 +395,14 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
88F92BAA28C67F8B0078BEC4 /* XCRemoteSwiftPackageReference "BinaryCodable" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/christophhagen/BinaryCodable";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 1.0.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
E234995A284E1D02002B55F8 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */ = {
|
E234995A284E1D02002B55F8 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/SFSafeSymbols/SFSafeSymbols";
|
repositoryURL = "https://github.com/SFSafeSymbols/SFSafeSymbols";
|
||||||
@ -403,27 +411,19 @@
|
|||||||
minimumVersion = 3.0.0;
|
minimumVersion = 3.0.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
E2349966284F78E3002B55F8 /* XCRemoteSwiftPackageReference "Push-iOS" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://christophhagen.de/git/ch/Push-iOS";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMajorVersion;
|
|
||||||
minimumVersion = 0.2.0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
88F92BAB28C67F8B0078BEC4 /* BinaryCodable */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 88F92BAA28C67F8B0078BEC4 /* XCRemoteSwiftPackageReference "BinaryCodable" */;
|
||||||
|
productName = BinaryCodable;
|
||||||
|
};
|
||||||
E234995B284E1D02002B55F8 /* SFSafeSymbols */ = {
|
E234995B284E1D02002B55F8 /* SFSafeSymbols */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = E234995A284E1D02002B55F8 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */;
|
package = E234995A284E1D02002B55F8 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */;
|
||||||
productName = SFSafeSymbols;
|
productName = SFSafeSymbols;
|
||||||
};
|
};
|
||||||
E2349967284F78E3002B55F8 /* Push */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = E2349966284F78E3002B55F8 /* XCRemoteSwiftPackageReference "Push-iOS" */;
|
|
||||||
productName = Push;
|
|
||||||
};
|
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = E29A7E3B284B6143000B908A /* Project object */;
|
rootObject = E29A7E3B284B6143000B908A /* Project object */;
|
||||||
|
@ -1,39 +1,12 @@
|
|||||||
{
|
{
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "apnswift",
|
"identity" : "binarycodable",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/swift-server-community/APNSwift.git",
|
"location" : "https://github.com/christophhagen/BinaryCodable",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "99a3c7bb5fd211009438fb386d18c94bb2f63b17",
|
"revision" : "2049887d460c0101e69922271a54a567dbf31e2c",
|
||||||
"version" : "4.0.0"
|
"version" : "1.2.2"
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "jwt-kit",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/vapor/jwt-kit.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "3537dd319dfbcc403a5165d8c19c4834e8e64730",
|
|
||||||
"version" : "4.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "push-definitions",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://christophhagen.de/git/ch/Push-Definitions.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "3a4e93889b0cb5f500eeccd7ba2a099d0f3862f0",
|
|
||||||
"version" : "1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "push-ios",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://christophhagen.de/git/ch/Push-iOS",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "1131cb17c6114a0b41263be1b5788188548adc41",
|
|
||||||
"version" : "0.3.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -44,51 +17,6 @@
|
|||||||
"revision" : "c8c33d947d8a1c883aa19fd24e14fd738b06e369",
|
"revision" : "c8c33d947d8a1c883aa19fd24e14fd738b06e369",
|
||||||
"version" : "3.3.2"
|
"version" : "3.3.2"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-crypto",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-crypto.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "d9825fa541df64b1a7b182178d61b9a82730d01f",
|
|
||||||
"version" : "2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-log",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-log.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "5d66f7ba25daf4f94100e7022febf3c75e37a6c7",
|
|
||||||
"version" : "1.4.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-nio",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-nio.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "124119f0bb12384cef35aa041d7c3a686108722d",
|
|
||||||
"version" : "2.40.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-nio-http2",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-nio-http2.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "72bcaf607b40d7c51044f65b0f5ed8581a911832",
|
|
||||||
"version" : "1.21.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-nio-ssl",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-nio-ssl.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "1750873bce84b4129b5303655cce2c3d35b9ed3a",
|
|
||||||
"version" : "2.19.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version" : 2
|
"version" : 2
|
||||||
|
BIN
FlurSchnaps.xcodeproj/project.xcworkspace/xcuserdata/imac.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
BIN
FlurSchnaps.xcodeproj/project.xcworkspace/xcuserdata/imac.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
Binary file not shown.
@ -1,61 +1,18 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import APNSwift
|
|
||||||
import Push
|
|
||||||
import PushMessageDefinitions
|
|
||||||
import SFSafeSymbols
|
import SFSafeSymbols
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
|
|
||||||
@AppStorage("pushToken")
|
@AppStorage("pushToken")
|
||||||
var pushToken: PushToken?
|
var pushToken: String?
|
||||||
|
|
||||||
var hasPushToken: Bool {
|
var hasPushToken: Bool {
|
||||||
pushToken != nil
|
pushToken != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@AppStorage("authToken")
|
|
||||||
var authToken: AuthenticationToken?
|
|
||||||
|
|
||||||
@State
|
|
||||||
var isConfirmed = false
|
|
||||||
|
|
||||||
@State
|
@State
|
||||||
var hasNotificationPermissions: Bool? = nil
|
var hasNotificationPermissions: Bool? = nil
|
||||||
|
|
||||||
@State
|
|
||||||
var showDeviceList = false
|
|
||||||
|
|
||||||
@State
|
|
||||||
var api: PushClient?
|
|
||||||
|
|
||||||
@AppStorage("server")
|
|
||||||
var server: String = ""
|
|
||||||
|
|
||||||
@AppStorage("application")
|
|
||||||
var application = ""
|
|
||||||
|
|
||||||
@AppStorage("deviceName")
|
|
||||||
var deviceName: String = ""
|
|
||||||
|
|
||||||
@State
|
|
||||||
var deviceList: [DeviceRegistration] = []
|
|
||||||
|
|
||||||
var couldBeRegistered: Bool {
|
|
||||||
pushToken != nil && authToken != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
@AppStorage("pushTitle")
|
|
||||||
var pushMessageTitle: String = ""
|
|
||||||
|
|
||||||
@AppStorage("pushBody")
|
|
||||||
var pushMessageText: String = ""
|
|
||||||
|
|
||||||
@State
|
|
||||||
var includeOwnDeviceInPush = false
|
|
||||||
|
|
||||||
var canSendNotification: Bool {
|
|
||||||
isConfirmed && (includeOwnDeviceInPush || deviceList.count > 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func statusView(_ state: Bool?) -> some View {
|
func statusView(_ state: Bool?) -> some View {
|
||||||
let symbol: SFSymbol
|
let symbol: SFSymbol
|
||||||
@ -95,6 +52,16 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@State
|
||||||
|
private var didTapText = false
|
||||||
|
|
||||||
|
var tapFootnote: String {
|
||||||
|
if didTapText {
|
||||||
|
return "Copied to clipboard"
|
||||||
|
}
|
||||||
|
return "Double tap to copy token"
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
VStack(spacing: 8) {
|
VStack(spacing: 8) {
|
||||||
@ -106,21 +73,23 @@ struct ContentView: View {
|
|||||||
statusView(hasNotificationPermissions)
|
statusView(hasNotificationPermissions)
|
||||||
Text("notification-permissions-title")
|
Text("notification-permissions-title")
|
||||||
}
|
}
|
||||||
HStack {
|
Spacer()
|
||||||
statusView(authToken != nil)
|
if let token = pushToken {
|
||||||
Text("push-server-registration-title")
|
Text("push-token-title")
|
||||||
}
|
Text(token)
|
||||||
HStack {
|
.font(.body.monospaced())
|
||||||
statusView(deviceList.count > 1)
|
.padding()
|
||||||
Text("other-devices-title")
|
.onTapGesture(count: 2, perform: didDoubleTapToken)
|
||||||
}
|
Text(tapFootnote)
|
||||||
if pushToken == nil {
|
.font(.footnote)
|
||||||
|
} else {
|
||||||
Text("register-for-remote-notifications-text")
|
Text("register-for-remote-notifications-text")
|
||||||
.padding()
|
.padding()
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
Button("register-for-remote-notifications-button", action: registerForRemoteNotifications)
|
Button("register-for-remote-notifications-button", action: registerForRemoteNotifications)
|
||||||
.padding()
|
.padding()
|
||||||
} else if hasNotificationPermissions == nil {
|
}
|
||||||
|
if hasNotificationPermissions == nil {
|
||||||
Button("request-notification-permission-button", action: requestNotificationPermission)
|
Button("request-notification-permission-button", action: requestNotificationPermission)
|
||||||
.padding()
|
.padding()
|
||||||
} else if hasNotificationPermissions == false {
|
} else if hasNotificationPermissions == false {
|
||||||
@ -129,70 +98,26 @@ struct ContentView: View {
|
|||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
Button("no-notification-permissions-button", action: openNotificationSettings)
|
Button("no-notification-permissions-button", action: openNotificationSettings)
|
||||||
.padding()
|
.padding()
|
||||||
} else if authToken == nil {
|
|
||||||
Text("register-device-text")
|
|
||||||
.padding()
|
|
||||||
TextEntryField("Server url", placeholder: "register-device-server-placeholder", symbol: .network, showClearButton: true, text: $server)
|
|
||||||
.padding(.horizontal, 50)
|
|
||||||
.padding(.top)
|
|
||||||
TextEntryField("Application", placeholder: "register-device-application-placeholder", symbol: .questionmarkApp, showClearButton: true, text: $application)
|
|
||||||
.padding(.horizontal, 50)
|
|
||||||
.padding(.top)
|
|
||||||
TextEntryField("Device name", placeholder: "register-device-name-placeholder", symbol: .iphone, text: $deviceName)
|
|
||||||
.padding(.horizontal, 50)
|
|
||||||
.padding(.top)
|
|
||||||
Button("register-device-button", action: register)
|
|
||||||
.disabled(pushToken == nil || authToken != nil || deviceName.isEmpty)
|
|
||||||
.padding()
|
|
||||||
} else {
|
|
||||||
Text("push-message-description")
|
|
||||||
.padding()
|
|
||||||
TextEntryField("Push title", placeholder: "push-message-title-placeholder", symbol: .bubbleLeft, showClearButton: true, text: $pushMessageTitle)
|
|
||||||
.padding(.horizontal, 50)
|
|
||||||
.disabled(!isConfirmed)
|
|
||||||
TextEntryField("Push text", placeholder: "push-message-placeholder", symbol: .textformat, showClearButton: true, text: $pushMessageText)
|
|
||||||
.padding(.horizontal, 50)
|
|
||||||
.disabled(!isConfirmed)
|
|
||||||
Toggle("toggle-include-own-device-text", isOn: $includeOwnDeviceInPush)
|
|
||||||
.padding(.horizontal, 50)
|
|
||||||
.padding(.top)
|
|
||||||
Button("send-notification-button", action: sendPush)
|
|
||||||
.disabled(!canSendNotification)
|
|
||||||
.padding()
|
|
||||||
Button("show-device-list-button", action: showDevices)
|
|
||||||
.disabled(!isConfirmed)
|
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.navigationTitle("FlurSchnaps")
|
.navigationTitle("FlurSchnaps")
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $showDeviceList) {
|
.onAppear {
|
||||||
if let push = pushToken, let auth = authToken, let api = api {
|
|
||||||
DeviceList(pushToken: push,
|
|
||||||
authToken: auth,
|
|
||||||
api: api,
|
|
||||||
isPresented: $showDeviceList,
|
|
||||||
devices: deviceList)
|
|
||||||
}
|
|
||||||
}.onAppear {
|
|
||||||
startPeriodicUpdates()
|
startPeriodicUpdates()
|
||||||
if server == "" {
|
|
||||||
server = "https://christophhagen.de/push"
|
|
||||||
}
|
|
||||||
if application == "" {
|
|
||||||
application = "FlurSchnaps"
|
|
||||||
}
|
|
||||||
if pushMessageTitle == "" {
|
|
||||||
pushMessageTitle = "Flur-Schnaps"
|
|
||||||
}
|
|
||||||
if pushMessageText == "" {
|
|
||||||
pushMessageText = "Du hast 30 Sekunden, um im Flur zu erscheinen"
|
|
||||||
}
|
|
||||||
}.onDisappear {
|
}.onDisappear {
|
||||||
stopPeriodicUpdates()
|
stopPeriodicUpdates()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func didDoubleTapToken() {
|
||||||
|
UIPasteboard.general
|
||||||
|
.setValue(pushToken!, forPasteboardType: UTType.plainText.identifier)
|
||||||
|
didTapText = true
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) {
|
||||||
|
self.didTapText = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@State
|
@State
|
||||||
private var timer: Timer?
|
private var timer: Timer?
|
||||||
@ -211,14 +136,9 @@ struct ContentView: View {
|
|||||||
timer?.invalidate()
|
timer?.invalidate()
|
||||||
timer = nil
|
timer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateState() {
|
private func updateState() {
|
||||||
updateNotificationPermissionState()
|
updateNotificationPermissionState()
|
||||||
if isConfirmed {
|
|
||||||
updateDeviceList()
|
|
||||||
} else if couldBeRegistered {
|
|
||||||
checkPushRegistrationStatus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerForRemoteNotifications() {
|
func registerForRemoteNotifications() {
|
||||||
@ -239,108 +159,6 @@ struct ContentView: View {
|
|||||||
UIApplication.shared.open(appSettings)
|
UIApplication.shared.open(appSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func register() {
|
|
||||||
guard let token = pushToken else {
|
|
||||||
print("No token to register")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let url = URL(string: server) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let api = PushClient(server: url, application: application)
|
|
||||||
self.api = api
|
|
||||||
let name = deviceName
|
|
||||||
Task {
|
|
||||||
print("Registering...")
|
|
||||||
guard let auth = await api.register(token: token, name: name) else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
authToken = nil
|
|
||||||
isConfirmed = false
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
print("Registered")
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
authToken = auth
|
|
||||||
isConfirmed = false
|
|
||||||
updateDeviceList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkPushRegistrationStatus() {
|
|
||||||
guard let token = pushToken,
|
|
||||||
let authToken = authToken,
|
|
||||||
let api = api else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Task {
|
|
||||||
let confirmed = await api.isConfirmed(token: token, authentication: authToken)
|
|
||||||
if !confirmed {
|
|
||||||
print(token.base64EncodedString())
|
|
||||||
print(authToken.base64EncodedString())
|
|
||||||
}
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
isConfirmed = confirmed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateDeviceList() {
|
|
||||||
guard let authToken = authToken,
|
|
||||||
let pushToken = pushToken,
|
|
||||||
let api = api else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Task {
|
|
||||||
let devices = await api.getDeviceList(pushToken: pushToken, authToken: authToken)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.deviceList = devices ?? []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func showDevices() {
|
|
||||||
showDeviceList = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendPush() {
|
|
||||||
guard let authToken = authToken,
|
|
||||||
let pushToken = pushToken,
|
|
||||||
let api = api else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var recipients = deviceList.map { $0.pushToken }
|
|
||||||
guard recipients.count > 0 else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !includeOwnDeviceInPush {
|
|
||||||
recipients = recipients.filter { $0 != pushToken }
|
|
||||||
}
|
|
||||||
let body = pushMessageText
|
|
||||||
let alert = APNSwiftAlert(
|
|
||||||
title: pushMessageTitle,
|
|
||||||
body: body)
|
|
||||||
let payload = APNSwiftPayload(
|
|
||||||
alert: alert,
|
|
||||||
sound: .normal("default"))
|
|
||||||
let content = PushMessage(
|
|
||||||
recipients: recipients,
|
|
||||||
payload: payload,
|
|
||||||
pushType: .alert)
|
|
||||||
let sender = DeviceAuthentication(
|
|
||||||
pushToken: pushToken,
|
|
||||||
authentication: authToken)
|
|
||||||
let message = AuthenticatedPushMessage(
|
|
||||||
sender: sender,
|
|
||||||
message: content)
|
|
||||||
Task {
|
|
||||||
let sent = await api.send(push: message)
|
|
||||||
print("Sent push message: \(sent)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContentView_Previews: PreviewProvider {
|
struct ContentView_Previews: PreviewProvider {
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
import SwiftUI
|
|
||||||
import PushMessageDefinitions
|
|
||||||
import Push
|
|
||||||
|
|
||||||
extension String {
|
|
||||||
|
|
||||||
var nonEmpty: String? {
|
|
||||||
isEmpty ? nil : self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DeviceList: View {
|
|
||||||
|
|
||||||
let pushToken: PushToken
|
|
||||||
|
|
||||||
let authToken: AuthenticationToken
|
|
||||||
|
|
||||||
let api: PushClient
|
|
||||||
|
|
||||||
@Binding
|
|
||||||
var isPresented: Bool
|
|
||||||
|
|
||||||
@State
|
|
||||||
var devices: [DeviceRegistration]
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
NavigationView {
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
List(devices) { device in
|
|
||||||
HStack {
|
|
||||||
Text(device.name.nonEmpty ?? "Device")
|
|
||||||
.font(.headline)
|
|
||||||
Spacer()
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text(device.application)
|
|
||||||
.font(.footnote)
|
|
||||||
.fontWeight(.bold)
|
|
||||||
.padding(.bottom, 2)
|
|
||||||
Text(device.pushToken.prefix(5).hexEncoded + "...")
|
|
||||||
.font(.caption)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.refreshable {
|
|
||||||
await updateList()
|
|
||||||
}
|
|
||||||
}.toolbar {
|
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
|
||||||
Button(action: dismiss) {
|
|
||||||
Text("Cancel")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.navigationBarTitle("device-list-title")
|
|
||||||
}.onAppear() {
|
|
||||||
Task {
|
|
||||||
await updateList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateList() async {
|
|
||||||
let devices = await api.getDeviceList(pushToken: pushToken, authToken: authToken)
|
|
||||||
print("Updated device list: \(devices?.count ?? -1)")
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.devices = devices ?? []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func dismiss() {
|
|
||||||
isPresented = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DeviceList_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
DeviceList(pushToken: Data(repeating: 42, count: 32),
|
|
||||||
authToken: Data(repeating: 42, count: 16),
|
|
||||||
api: .init(server: URL(string: "https://christophhagen.de/push")!, application: "some"),
|
|
||||||
isPresented: .constant(true),
|
|
||||||
devices: [DeviceRegistration(
|
|
||||||
pushToken: Data([1,2,3,4,5]),
|
|
||||||
application: "CC Messenger",
|
|
||||||
name: "Some")])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension DeviceRegistration: Identifiable {
|
|
||||||
|
|
||||||
public var id: String {
|
|
||||||
pushToken.prefix(5).hexEncoded
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import BinaryCodable
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct FlurSchnapsApp: App {
|
struct FlurSchnapsApp: App {
|
||||||
@ -15,12 +16,11 @@ struct FlurSchnapsApp: App {
|
|||||||
|
|
||||||
class AppDelegate: NSObject, UIApplicationDelegate {
|
class AppDelegate: NSObject, UIApplicationDelegate {
|
||||||
|
|
||||||
@AppStorage("pushToken")
|
@AppStorage("deviceToken")
|
||||||
var pushToken: Data?
|
var pushToken: Data?
|
||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
||||||
|
|
||||||
|
|
||||||
// For iOS 10 display notification (sent via APNS)
|
// For iOS 10 display notification (sent via APNS)
|
||||||
UNUserNotificationCenter.current().delegate = self
|
UNUserNotificationCenter.current().delegate = self
|
||||||
|
|
||||||
@ -31,8 +31,9 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
|
|
||||||
func application(_ application: UIApplication,
|
func application(_ application: UIApplication,
|
||||||
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
||||||
print("Registered with token: \(deviceToken)")
|
let token = deviceToken.hexEncoded
|
||||||
self.pushToken = deviceToken
|
print("Registered for remote notifications with token: \(token)")
|
||||||
|
uploadNewDeviceToken(deviceToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
|
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
|
||||||
@ -42,6 +43,47 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
|
|
||||||
completionHandler(UIBackgroundFetchResult.newData)
|
completionHandler(UIBackgroundFetchResult.newData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func uploadNewDeviceToken(_ token: Data) {
|
||||||
|
if token == pushToken {
|
||||||
|
// Device token unchanged
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Task {
|
||||||
|
let tokenUpload = TokenUpload(currentToken: token, previousToken: pushToken)
|
||||||
|
let data: Data
|
||||||
|
do {
|
||||||
|
data = try BinaryEncoder().encode(tokenUpload)
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
print("Failed to encode token upload")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let result: URLResponse
|
||||||
|
do {
|
||||||
|
var request = URLRequest(url: URL(string: "https://christophhagen.de/schnaps/token")!)
|
||||||
|
request.httpMethod = "POST"
|
||||||
|
(_, result) = try await URLSession.shared.upload(for: request, from: data)
|
||||||
|
} catch {
|
||||||
|
print("Failed to upload token: \(error)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let response = result as? HTTPURLResponse else {
|
||||||
|
print("Invalid response \(result)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard response.statusCode == 200 else {
|
||||||
|
print("Invalid response to token upload: \(response.statusCode)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
print("Push token uploaded")
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.pushToken = token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Upload new token, possibly with old one
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppDelegate: UNUserNotificationCenterDelegate {
|
extension AppDelegate: UNUserNotificationCenterDelegate {
|
||||||
|
13
FlurSchnaps/TokenUpload.swift
Normal file
13
FlurSchnaps/TokenUpload.swift
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct TokenUpload: Codable {
|
||||||
|
|
||||||
|
let currentToken: Data
|
||||||
|
|
||||||
|
let previousToken: Data?
|
||||||
|
|
||||||
|
enum CodingKeys: Int, CodingKey {
|
||||||
|
case currentToken = 1
|
||||||
|
case previousToken = 2
|
||||||
|
}
|
||||||
|
}
|
@ -39,3 +39,5 @@
|
|||||||
"device-list-title" = "Geräte";
|
"device-list-title" = "Geräte";
|
||||||
|
|
||||||
"show-device-list-button" = "Liste einzeigen";
|
"show-device-list-button" = "Liste einzeigen";
|
||||||
|
|
||||||
|
"push-token-title" = "Geräte-Identifikator";
|
||||||
|
@ -39,3 +39,5 @@
|
|||||||
"device-list-title" = "Devices";
|
"device-list-title" = "Devices";
|
||||||
|
|
||||||
"show-device-list-button" = "Show device list";
|
"show-device-list-button" = "Show device list";
|
||||||
|
|
||||||
|
"push-token-title" = "Push token";
|
||||||
|
Loading…
Reference in New Issue
Block a user