Merge pull request #10 from Hext123/main

完成全部主体功能, iOS已经可以正常使用
This commit is contained in:
Easy 2022-01-10 21:35:43 +08:00 committed by GitHub
commit 52038fbd7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 763 additions and 141 deletions

View File

@ -7,6 +7,10 @@ use_frameworks!
def commonPods
# Pods for common
pod 'Moya', '~> 15.0'
pod 'SDWebImageSwiftUI', '~> 2.0.2'
pod 'KRProgressHUD', '~> 3.4.7'
# pod 'WoodPeckeriOS', :configurations => ['Debug']
end
target 'PushDeer' do

View File

@ -1,22 +1,40 @@
PODS:
- Alamofire (5.5.0)
- KRActivityIndicatorView (3.0.7)
- KRProgressHUD (3.4.7):
- KRActivityIndicatorView (= 3.0.7)
- Moya (15.0.0):
- Moya/Core (= 15.0.0)
- Moya/Core (15.0.0):
- Alamofire (~> 5.0)
- SDWebImage (5.12.1):
- SDWebImage/Core (= 5.12.1)
- SDWebImage/Core (5.12.1)
- SDWebImageSwiftUI (2.0.2):
- SDWebImage (~> 5.10)
DEPENDENCIES:
- KRProgressHUD (~> 3.4.7)
- Moya (~> 15.0)
- SDWebImageSwiftUI (~> 2.0.2)
SPEC REPOS:
trunk:
- Alamofire
- KRActivityIndicatorView
- KRProgressHUD
- Moya
- SDWebImage
- SDWebImageSwiftUI
SPEC CHECKSUMS:
Alamofire: 1c4fb5369c3fe93d2857c780d8bbe09f06f97e7c
KRActivityIndicatorView: ad69e89c4ce40c986cf580595be4829dcad0e35a
KRProgressHUD: a248f0bc6c9c2aed40a37b76e03ffecc7f85c887
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
SDWebImage: 4dc3e42d9ec0c1028b960a33ac6b637bb432207b
SDWebImageSwiftUI: 8a3923c95108312b03a599ec1498754af55a6819
PODFILE CHECKSUM: 71abdebce610eefe1a0299d0a0bd00a463b0a79c
PODFILE CHECKSUM: e462e86a9cce18b92c573f662ef405e7091cd912
COCOAPODS: 1.11.2

View File

@ -25,13 +25,16 @@
52450F432784943F003652D8 /* HttpRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450F412784943F003652D8 /* HttpRequest.swift */; };
52483FC2277ED6D5003A100E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52483FC1277ED6D5003A100E /* AppDelegate.swift */; };
52483FC3277ED6D5003A100E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52483FC1277ED6D5003A100E /* AppDelegate.swift */; };
5287FF52278ADC8C00249A0E /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = 5287FF51278ADC8C00249A0E /* MarkdownUI */; };
5287FF54278AEA5B00249A0E /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = 5287FF53278AEA5B00249A0E /* MarkdownUI */; };
5287FF59278B3AAE00249A0E /* HToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5287FF58278B3AAE00249A0E /* HToast.swift */; };
5287FF5A278B3AAE00249A0E /* HToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5287FF58278B3AAE00249A0E /* HToast.swift */; };
5292F4F92776BC7900B9A7BB /* PushDeerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5292F4F82776BC7900B9A7BB /* PushDeerApp.swift */; };
5292F4FB2776BC7900B9A7BB /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5292F4FA2776BC7900B9A7BB /* ContentView.swift */; };
5292F4FD2776BC7A00B9A7BB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5292F4FC2776BC7A00B9A7BB /* Assets.xcassets */; };
5292F5002776BC7A00B9A7BB /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5292F4FF2776BC7A00B9A7BB /* Preview Assets.xcassets */; };
52B8CF5F277DE660004CB680 /* AppleSignInButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B8CF5E277DE660004CB680 /* AppleSignInButton.swift */; };
52B8CF67277E0B44004CB680 /* PushDeerClipApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B8CF66277E0B44004CB680 /* PushDeerClipApp.swift */; };
52B8CF69277E0B44004CB680 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B8CF68277E0B44004CB680 /* ContentView.swift */; };
52B8CF6B277E0B46004CB680 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52B8CF6A277E0B46004CB680 /* Assets.xcassets */; };
52B8CF6E277E0B46004CB680 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52B8CF6D277E0B46004CB680 /* Preview Assets.xcassets */; };
52B8CF73277E0B46004CB680 /* PushDeerClip.app in Embed App Clips */ = {isa = PBXBuildFile; fileRef = 52B8CF64277E0B44004CB680 /* PushDeerClip.app */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@ -58,6 +61,7 @@
52F0243F277737470071D861 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F0243E277737470071D861 /* LoginView.swift */; };
52F2C223277961D7006F08DC /* SettingsItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F2C222277961D7006F08DC /* SettingsItemView.swift */; };
52F40D2F277CA05600766C24 /* MessageItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F40D2E277CA05600766C24 /* MessageItemView.swift */; };
52FBA09427874879003308C2 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5292F4FA2776BC7900B9A7BB /* ContentView.swift */; };
64B0C15E70CCC382B480F76E /* Pods_PushDeer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E380A18349DE4D26071E913E /* Pods_PushDeer.framework */; };
/* End PBXBuildFile section */
@ -99,6 +103,7 @@
52450F412784943F003652D8 /* HttpRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpRequest.swift; sourceTree = "<group>"; };
52450F442784A95D003652D8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
52483FC1277ED6D5003A100E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
5287FF58278B3AAE00249A0E /* HToast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HToast.swift; sourceTree = "<group>"; };
5292F4F52776BC7900B9A7BB /* PushDeer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PushDeer.app; sourceTree = BUILT_PRODUCTS_DIR; };
5292F4F82776BC7900B9A7BB /* PushDeerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushDeerApp.swift; sourceTree = "<group>"; };
5292F4FA2776BC7900B9A7BB /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@ -107,7 +112,6 @@
52B8CF5E277DE660004CB680 /* AppleSignInButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleSignInButton.swift; sourceTree = "<group>"; };
52B8CF64277E0B44004CB680 /* PushDeerClip.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PushDeerClip.app; sourceTree = BUILT_PRODUCTS_DIR; };
52B8CF66277E0B44004CB680 /* PushDeerClipApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushDeerClipApp.swift; sourceTree = "<group>"; };
52B8CF68277E0B44004CB680 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
52B8CF6A277E0B46004CB680 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
52B8CF6D277E0B46004CB680 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
52B8CF6F277E0B46004CB680 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -134,6 +138,7 @@
buildActionMask = 2147483647;
files = (
64B0C15E70CCC382B480F76E /* Pods_PushDeer.framework in Frameworks */,
5287FF52278ADC8C00249A0E /* MarkdownUI in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -142,6 +147,7 @@
buildActionMask = 2147483647;
files = (
4812F19BB0BFEFE089BC253E /* Pods_PushDeerClip.framework in Frameworks */,
5287FF54278AEA5B00249A0E /* MarkdownUI in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -227,6 +233,7 @@
52B8CF5D277DE5FF004CB680 /* Common */ = {
isa = PBXGroup;
children = (
5287FF58278B3AAE00249A0E /* HToast.swift */,
52B8CF5E277DE660004CB680 /* AppleSignInButton.swift */,
);
path = Common;
@ -236,7 +243,6 @@
isa = PBXGroup;
children = (
52B8CF66277E0B44004CB680 /* PushDeerClipApp.swift */,
52B8CF68277E0B44004CB680 /* ContentView.swift */,
52B8CF6A277E0B46004CB680 /* Assets.xcassets */,
52B8CF6F277E0B46004CB680 /* Info.plist */,
52B8CF70277E0B46004CB680 /* PushDeerClip.entitlements */,
@ -311,6 +317,9 @@
52B8CF72277E0B46004CB680 /* PBXTargetDependency */,
);
name = PushDeer;
packageProductDependencies = (
5287FF51278ADC8C00249A0E /* MarkdownUI */,
);
productName = PushDeer;
productReference = 5292F4F52776BC7900B9A7BB /* PushDeer.app */;
productType = "com.apple.product-type.application";
@ -330,6 +339,9 @@
dependencies = (
);
name = PushDeerClip;
packageProductDependencies = (
5287FF53278AEA5B00249A0E /* MarkdownUI */,
);
productName = PushDeerClip;
productReference = 52B8CF64277E0B44004CB680 /* PushDeerClip.app */;
productType = "com.apple.product-type.application.on-demand-install-capable";
@ -361,6 +373,9 @@
Base,
);
mainGroup = 5292F4EC2776BC7900B9A7BB;
packageReferences = (
5287FF50278ADC8C00249A0E /* XCRemoteSwiftPackageReference "MarkdownUI" */,
);
productRefGroup = 5292F4F62776BC7900B9A7BB /* Products */;
projectDirPath = "";
projectRoot = "";
@ -490,6 +505,7 @@
52B8CF5F277DE660004CB680 /* AppleSignInButton.swift in Sources */,
52163EB92777417900594190 /* KeyListView.swift in Sources */,
5292F4FB2776BC7900B9A7BB /* ContentView.swift in Sources */,
5287FF59278B3AAE00249A0E /* HToast.swift in Sources */,
52450F3B278491F8003652D8 /* AppState.swift in Sources */,
5292F4F92776BC7900B9A7BB /* PushDeerApp.swift in Sources */,
52483FC2277ED6D5003A100E /* AppDelegate.swift in Sources */,
@ -509,7 +525,6 @@
buildActionMask = 2147483647;
files = (
52B8CF82277E0C06004CB680 /* AppleSignInButton.swift in Sources */,
52B8CF69277E0B44004CB680 /* ContentView.swift in Sources */,
52B8CF78277E0BF1004CB680 /* MainView.swift in Sources */,
52B8CF79277E0BFB004CB680 /* DeviceListView.swift in Sources */,
52B8CF84277E0C12004CB680 /* CardView.swift in Sources */,
@ -520,11 +535,13 @@
52B8CF81277E0BFB004CB680 /* LoginView.swift in Sources */,
52B8CF7F277E0BFB004CB680 /* SettingsItemView.swift in Sources */,
52450F3C278491F8003652D8 /* AppState.swift in Sources */,
5287FF5A278B3AAE00249A0E /* HToast.swift in Sources */,
52B8CF80277E0BFB004CB680 /* MessageItemView.swift in Sources */,
52483FC3277ED6D5003A100E /* AppDelegate.swift in Sources */,
52450F3927848243003652D8 /* PushDeerApi.swift in Sources */,
52B8CF86277E0C12004CB680 /* BaseNavigationView.swift in Sources */,
52B8CF67277E0B44004CB680 /* PushDeerClipApp.swift in Sources */,
52FBA09427874879003308C2 /* ContentView.swift in Sources */,
52B8CF85277E0C12004CB680 /* Line.swift in Sources */,
52450F402784923D003652D8 /* Result.swift in Sources */,
52450F432784943F003652D8 /* HttpRequest.swift in Sources */,
@ -739,6 +756,10 @@
DEVELOPMENT_ASSET_PATHS = "\"PushDeerClip/Preview Content\"";
DEVELOPMENT_TEAM = Y47WTLML2S;
ENABLE_PREVIEWS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"APPCLIP=1",
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PushDeerClip/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = PushDeer;
@ -753,6 +774,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
OTHER_SWIFT_FLAGS = "$(inherited) -D APPCLIP";
PRODUCT_BUNDLE_IDENTIFIER = com.wskfz.pushdeer.ios.Clip;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
@ -773,6 +795,10 @@
DEVELOPMENT_ASSET_PATHS = "\"PushDeerClip/Preview Content\"";
DEVELOPMENT_TEAM = Y47WTLML2S;
ENABLE_PREVIEWS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"APPCLIP=1",
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PushDeerClip/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = PushDeer;
@ -787,6 +813,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
OTHER_SWIFT_FLAGS = "$(inherited) -D APPCLIP";
PRODUCT_BUNDLE_IDENTIFIER = com.wskfz.pushdeer.ios.Clip;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
@ -826,6 +853,30 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
5287FF50278ADC8C00249A0E /* XCRemoteSwiftPackageReference "MarkdownUI" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/gonzalezreal/MarkdownUI";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.5.2;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
5287FF51278ADC8C00249A0E /* MarkdownUI */ = {
isa = XCSwiftPackageProductDependency;
package = 5287FF50278ADC8C00249A0E /* XCRemoteSwiftPackageReference "MarkdownUI" */;
productName = MarkdownUI;
};
5287FF53278AEA5B00249A0E /* MarkdownUI */ = {
isa = XCSwiftPackageProductDependency;
package = 5287FF50278ADC8C00249A0E /* XCRemoteSwiftPackageReference "MarkdownUI" */;
productName = MarkdownUI;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 5292F4ED2776BC7900B9A7BB /* Project object */;
}

View File

@ -0,0 +1,70 @@
{
"object": {
"pins": [
{
"package": "AttributedText",
"repositoryURL": "https://github.com/gonzalezreal/AttributedText",
"state": {
"branch": null,
"revision": "c345033e22d5a1cd0e9fe0ec405cc809a8349586",
"version": "0.3.1"
}
},
{
"package": "combine-schedulers",
"repositoryURL": "https://github.com/pointfreeco/combine-schedulers",
"state": {
"branch": null,
"revision": "4cf088c29a20f52be0f2ca54992b492c54e0076b",
"version": "0.5.3"
}
},
{
"package": "MarkdownUI",
"repositoryURL": "https://github.com/gonzalezreal/MarkdownUI",
"state": {
"branch": null,
"revision": "29d94710545952dd4c724cc2ca901848eef54ded",
"version": "0.5.2"
}
},
{
"package": "NetworkImage",
"repositoryURL": "https://github.com/gonzalezreal/NetworkImage",
"state": {
"branch": null,
"revision": "04167e81ed89a14b5da9b856ead306b969bb5abd",
"version": "3.1.2"
}
},
{
"package": "cmark",
"repositoryURL": "https://github.com/SwiftDocOrg/swift-cmark.git",
"state": {
"branch": null,
"revision": "9c8096a23f44794bde297452d87c455fc4f76d42",
"version": "0.29.0+20210102.9c8096a"
}
},
{
"package": "SwiftCommonMark",
"repositoryURL": "https://github.com/gonzalezreal/SwiftCommonMark",
"state": {
"branch": null,
"revision": "ed60da54305c244d0f77bc8d08495e04161e96a4",
"version": "0.1.2"
}
},
{
"package": "xctest-dynamic-overlay",
"repositoryURL": "https://github.com/pointfreeco/xctest-dynamic-overlay",
"state": {
"branch": null,
"revision": "50a70a9d3583fe228ce672e8923010c8df2deddd",
"version": "0.2.1"
}
}
]
},
"version": 1
}

View File

@ -9,9 +9,7 @@ import UIKit
import UserNotifications
class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
static var deviceToken: String = ""
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
let center = UNUserNotificationCenter.current()
@ -23,9 +21,7 @@ class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDele
Task {
// APP, , . ()
let result = try await HttpRequest.fake()
AppState.shared.token = result.token
HttpRequest.getDevices()
_ = try await HttpRequest.fake()
}
return true
@ -34,7 +30,7 @@ class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDele
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
print("deviceToken: ", deviceTokenString)
AppDelegate.deviceToken = deviceTokenString;
AppState.shared.deviceToken = deviceTokenString
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {

View File

@ -13,12 +13,12 @@ import AuthenticationServices
/// Apple , 仿 SwiftUI SignInWithAppleButton
///
/// 使? , SignInWithAppleButton macOS , iOS .
struct AppleSignInButton: UIViewRepresentable {
@MainActor struct AppleSignInButton: UIViewRepresentable {
var type = ASAuthorizationAppleIDButton.ButtonType.signIn
var style = ASAuthorizationAppleIDButton.Style.black
let onRequest: (ASAuthorizationAppleIDRequest) -> Void
let onCompletion: (Result<ASAuthorization, Error>) -> Void
let onCompletion: (Result<ASAuthorization, Error>) async -> Void
func makeUIView(context: Context) -> ASAuthorizationAppleIDButton {
let signInButton = ASAuthorizationAppleIDButton(type: type, style: style)
@ -40,11 +40,11 @@ struct AppleSignInButton: UIViewRepresentable {
class AppleSignInCoordinator: NSObject, ASAuthorizationControllerDelegate {
let onRequest: (ASAuthorizationAppleIDRequest) -> Void
let onCompletion: (Result<ASAuthorization, Error>) -> Void
let onCompletion: (Result<ASAuthorization, Error>) async -> Void
init(
onRequest: @escaping (ASAuthorizationAppleIDRequest) -> Void,
onCompletion: @escaping (Result<ASAuthorization, Error>) -> Void
onCompletion: @escaping (Result<ASAuthorization, Error>) async -> Void
) {
self.onRequest = onRequest
self.onCompletion = onCompletion
@ -61,11 +61,15 @@ class AppleSignInCoordinator: NSObject, ASAuthorizationControllerDelegate {
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
print(authorization.debugDescription)
onCompletion(.success(authorization))
Task {
await onCompletion(.success(authorization))
}
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
print(error.localizedDescription)
onCompletion(.failure(error))
Task {
await onCompletion(.failure(error))
}
}
}

View File

@ -0,0 +1,29 @@
//
// HToast.swift
// LotusDesktop
//
// Created by HEXT on 2020/9/26.
// Copyright © 2020 HEXT. All rights reserved.
//
import UIKit
import KRProgressHUD
struct HToast {
static func showText(_ msg: String) {
KRProgressHUD.showMessage(msg)
}
static func showSuccess(_ msg: String?) {
KRProgressHUD.showSuccess(withMessage: msg)
}
static func showError(_ msg: String?) {
KRProgressHUD.showError(withMessage: msg)
}
static func showLoading(_ msg: String?) {
KRProgressHUD.show(withMessage: msg)
}
static func dismiss() {
KRProgressHUD.dismiss()
}
}

View File

@ -8,9 +8,15 @@
import SwiftUI
struct ContentView: View {
@EnvironmentObject private var store: AppState
var body: some View {
// LoginView()
MainView()
if store.token.isEmpty {
LoginView()
} else {
MainView()
}
}
}

View File

@ -2,14 +2,24 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>APP为您提供保存图片到相册的功能</string>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSBonjourServices</key>
<array>
<string>_adhp._tcp</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
<key>NSLocalNetworkUsageDescription</key>
<string>APP需要访问本地网络以供调试(仅在开发时)</string>
</dict>
</plist>

View File

@ -20,6 +20,17 @@ struct TokenContent: Codable{
let token: String
}
struct UserInfoContent: Codable{
let id: Int
let name: String
let email: String
let apple_id: String
let wechat_id: String?
let level: Int
let created_at: String
let updated_at: String
}
struct DeviceItem: Codable, Identifiable{
let id: Int
let uid: String
@ -39,7 +50,10 @@ struct KeyContent: Codable{
struct KeyItem: Codable, Identifiable{
let id: Int
let name: String
let uid: String
let key: String
let created_at: String
}
struct MessageContent: Codable{
@ -52,8 +66,37 @@ struct MessageItem: Codable, Identifiable{
let text: String
let desp: String
let type: String
let created_at: String
}
struct ActionContent: Codable{
let message: String
}
struct PushResultContent: Codable{
let result: Array<String>
}
let dateFormatter = DateFormatter()
extension KeyItem {
var createdDateStr: String {
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"
let createdDate = dateFormatter.date(from: self.created_at)
dateFormatter.dateFormat = "yyyy/MM/dd"
return dateFormatter.string(from: createdDate ?? Date())
}
}
extension MessageItem {
var createdDateStr: String {
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"
let createdDate = dateFormatter.date(from: self.created_at) ?? Date()
if Calendar.current.isDateInToday(createdDate) {
dateFormatter.dateFormat = "HH:mm:ss"
} else {
dateFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss"
}
return dateFormatter.string(from: createdDate)
}
}

View File

@ -6,14 +6,86 @@
//
import Foundation
import AuthenticationServices
class AppState: ObservableObject {
@Published var token : String? = nil
/// token
@Published var token : String {
didSet {
UserDefaults.standard.set(token, forKey: "PushDeer_token")
}
}
///
@Published var devices: [DeviceItem] = []
/// key
@Published var keys: [KeyItem] = []
///
@Published var messages: [MessageItem] = []
@Published var tab_selected: Int = 1
@Published var device_token: String = ""
/// tab
@Published var tabSelectedIndex: Int {
didSet {
UserDefaults.standard.set(tabSelectedIndex, forKey: "PushDeer_tabSelectedIndex")
}
}
/// token
@Published var deviceToken: String = ""
///
@Published var userInfo: UserInfoContent?
/// UI
@Published var isShowTestPush: Bool {
didSet {
UserDefaults.standard.set(isShowTestPush, forKey: "PushDeer_isShowTestPush")
}
}
var isAppClip: Bool {
#if APPCLIP
return true
#else
return false
#endif
}
static let shared = AppState()
private init() {}
private init() {
let _token = UserDefaults.standard.string(forKey: "PushDeer_token")
let _tabSelectedIndex = UserDefaults.standard.integer(forKey: "PushDeer_tabSelectedIndex")
let _isShowTestPush = UserDefaults.standard.object(forKey: "PushDeer_isShowTestPush")
token = _token ?? ""
tabSelectedIndex = _tabSelectedIndex
isShowTestPush = _isShowTestPush as? Bool ?? true
}
func appleIdLogin(_ result: Result<ASAuthorization, Error>) async throws -> TokenContent {
switch result {
case let .success(authorization):
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
// IDAPP
print(appleIDCredential.user) // 000791.7a323f1326dd4674bc16d32fd6339875.1424
// emailfullName->Apple ID->->使AppleIDApp APP
print(appleIDCredential.email as Any) // easychen@qq.com
print(appleIDCredential.fullName as Any) // givenName: lijie familyName: chen
// JWTtoken.3, base64
let idToken = String(data:appleIDCredential.identityToken!, encoding: .utf8)
print(idToken as Any)
do {
//
let result = try await HttpRequest.login(idToken: idToken!)
print(result)
//
return result
} catch {
print(error)
}
}
case let .failure(error):
print(error)
}
//
throw NSError(domain: "登录失败", code: -1, userInfo: nil)
}
}

View File

@ -8,6 +8,7 @@
import Foundation
import Moya
@MainActor
struct HttpRequest {
static let provider = MoyaProvider<PushDeerApi>(callbackQueue: DispatchQueue.main)
@ -23,6 +24,9 @@ struct HttpRequest {
print(result)
if let content = result.content, result.code == 0 {
continuation.resume(returning: content)
} else if result.code == 80403 {
AppState.shared.token = ""
continuation.resume(throwing: NSError(domain: result.error ?? "接口报错", code: result.code, userInfo: nil))
} else {
continuation.resume(throwing: NSError(domain: result.error ?? "接口报错", code: result.code, userInfo: nil))
}
@ -46,10 +50,68 @@ struct HttpRequest {
return try await request(.login(idToken: idToken), resultType: TokenContent.self)
}
@MainActor static func getDevices() {
static func getUserInfo() async throws -> UserInfoContent {
return try await request(.getUserInfo(token: AppState.shared.token), resultType: UserInfoContent.self)
}
static func regDevice() async throws -> DeviceContent {
return try await request(.regDevice(
token: AppState.shared.token,
name: UIDevice.current.name,
device_id: AppState.shared.deviceToken,
is_clip: AppState.shared.isAppClip ? 1 : 0
), resultType: DeviceContent.self)
}
static func rmDevice(id: Int) async throws -> ActionContent {
return try await request(.rmDevice(token: AppState.shared.token, id: id), resultType: ActionContent.self)
}
static func getDevices() async throws -> DeviceContent {
return try await request(.getDevices(token: AppState.shared.token), resultType: DeviceContent.self)
}
static func loadDevices() {
_Concurrency.Task {
let result = try await request(.getDevices(token: AppState.shared.token ?? ""), resultType: DeviceContent.self)
let result = try await getDevices()
AppState.shared.devices = result.devices
}
}
static func genKey() async throws -> KeyContent {
return try await request(.genKey(token: AppState.shared.token), resultType: KeyContent.self)
}
static func regenKey(id: Int) async throws -> ActionContent {
return try await request(.regenKey(token: AppState.shared.token, id: id), resultType: ActionContent.self)
}
static func renameKey(id: Int, name: String) async throws -> ActionContent {
return try await request(.renameKey(token: AppState.shared.token, id: id, name: name), resultType: ActionContent.self)
}
static func rmKey(id: Int) async throws -> ActionContent {
return try await request(.rmKey(token: AppState.shared.token, id: id), resultType: ActionContent.self)
}
static func getKeys() async throws -> KeyContent {
return try await request(.getKeys(token: AppState.shared.token), resultType: KeyContent.self)
}
static func loadKeys() {
_Concurrency.Task {
let result = try await getKeys()
AppState.shared.keys = result.keys
}
}
static func push(pushkey: String, text: String, desp: String, type: String) async throws -> PushResultContent {
return try await request(.push(pushkey: pushkey, text: text, desp: desp, type: type), resultType: PushResultContent.self)
}
static func getMessages() async throws -> MessageContent {
return try await request(.getMessages(token: AppState.shared.token, limit: 100), resultType: MessageContent.self)
}
static func rmMessage(id: Int) async throws -> ActionContent {
return try await request(.rmMessage(token: AppState.shared.token, id: id), resultType: ActionContent.self)
}
}

View File

@ -12,6 +12,7 @@ enum PushDeerApi {
case fake
case login(idToken: String)
case getUserInfo(token: String)
case regDevice(token: String, name: String, device_id: String, is_clip: Int)
case getDevices(token: String)
@ -39,6 +40,8 @@ extension PushDeerApi: TargetType {
return "/login/fake"
case .login:
return "/login/idtoken"
case .getUserInfo:
return "/user/info"
case .regDevice:
return "/device/reg"
@ -79,6 +82,8 @@ extension PushDeerApi: TargetType {
return .requestParameters(parameters: [:], encoding: URLEncoding.queryString)
case let .login(idToken):
return .requestParameters(parameters: ["idToken": idToken], encoding: URLEncoding.queryString)
case let .getUserInfo(token):
return .requestParameters(parameters: ["token": token], encoding: URLEncoding.queryString)
case let .regDevice(token, name, device_id, is_clip):
return .requestParameters(parameters: ["token": token,"name": name, "device_id": device_id,"is_clip": is_clip], encoding: URLEncoding.queryString)

View File

@ -13,7 +13,7 @@ struct DeviceItemView: View {
var body: some View {
CardView {
HStack{
Image(systemName: "ipad.and.iphone")
Image(systemName: getSystemName(deviceName: name))
.resizable()
.scaledToFit()
.frame(width: 40, height: 40, alignment: .center)
@ -25,6 +25,37 @@ struct DeviceItemView: View {
.frame(height: 80)
}
}
func getSystemName(deviceName: String) -> String {
var deviceName = deviceName.lowercased()
deviceName = deviceName.replacingOccurrences(of: " ", with: "")
// if deviceName.contains("clip") {
// return "appclip"
// }
if deviceName.contains("iphone") {
return "iphone"
}
if deviceName.contains("ipad") {
return "ipad.landscape"
}
if deviceName.contains("macbook") {
return "laptopcomputer"
}
if deviceName.contains("imac") {
return "desktopcomputer"
}
if deviceName.contains("macpro") {
return "macpro.gen3"
}
if deviceName.contains("macmini") {
return "macmini"
}
if deviceName.contains("mac") {
return "macwindow"
}
return "ipad.and.iphone"
}
}
struct DeviceItemView_Previews: PreviewProvider {

View File

@ -14,13 +14,21 @@ struct DeviceListView: View {
BaseNavigationView(title: "设备") {
ScrollView {
LazyVStack(alignment: .center) {
ForEach(store.devices, id: \.id) { deviceItem in
ForEach(store.devices.reversed()) { deviceItem in
DeletableView(contentView: {
DeviceItemView(name: deviceItem.name)
DeviceItemView(name: getName(deviceItem: deviceItem))
}, deleteAction: {
store.devices.removeAll { _deviceItem in
_deviceItem.id == deviceItem.id
}
HToast.showSuccess("已删除")
Task {
do {
_ = try await HttpRequest.rmDevice(id: deviceItem.id)
} catch {
}
}
})
.padding(EdgeInsets(top: 18, leading: 26, bottom: 0, trailing: 24))
}
@ -28,8 +36,12 @@ struct DeviceListView: View {
}
}
.navigationBarItems(trailing: Button(action: {
withAnimation(.easeOut) {
// store.devices.insert(DeviceItem(), at: 0)
Task {
let devices = try await HttpRequest.regDevice().devices
withAnimation(.easeOut) {
store.devices = devices
}
HToast.showSuccess("已添加当前设备")
}
}, label: {
Image(systemName: "plus")
@ -37,13 +49,24 @@ struct DeviceListView: View {
}))
}
.onAppear {
HttpRequest.getDevices()
HttpRequest.loadDevices()
}
}
func getName(deviceItem: DeviceItem) -> String {
var name = deviceItem.name
if deviceItem.is_clip == 1 {
name += " [Clip]"
}
if deviceItem.device_id == store.deviceToken {
name += " (当前设备)"
}
return name
}
}
struct DeviceView_Previews: PreviewProvider {
static var previews: some View {
DeviceListView()
DeviceListView().environmentObject(AppState.shared)
}
}

View File

@ -7,6 +7,57 @@
import SwiftUI
struct KeyItemTextField: View {
let keyItem: KeyItem
@State private var value = ""
@EnvironmentObject private var store: AppState
init(keyItem: KeyItem) {
self.keyItem = keyItem
self._value = State(initialValue: keyItem.name)
}
func textField() -> some View {
TextField("输入key名称", text: $value, onCommit: {
Task {
do {
//
_ = try await HttpRequest.renameKey(id: keyItem.id, name: value)
HToast.showSuccess("已修改key名称")
// keyItem
let index = store.keys.firstIndex { _keyItem in
_keyItem.id == keyItem.id
}
if let index = index {
let _keyItem = store.keys[index]
// keyItem
store.keys[index] = KeyItem(
id: _keyItem.id,
name: value,
uid: _keyItem.uid,
key: _keyItem.key,
created_at: _keyItem.created_at
)
}
} catch {
}
}
})
.font(.system(size: 20))
.foregroundColor(Color.accentColor)
}
var body: some View {
if #available(iOS 15.0, *) {
textField()
.submitLabel(.done)
} else {
textField()
}
}
}
/// Key View
struct KeyItemView: View {
let keyItem: KeyItem
@ -18,14 +69,12 @@ struct KeyItemView: View {
.resizable()
.scaledToFit()
.frame(width: 38, height: 38)
Text("Key \(keyItem.id)")
.font(.system(size: 20))
.foregroundColor(Color.accentColor)
KeyItemTextField(keyItem: keyItem)
Spacer()
Image(systemName: "calendar")
.font(.system(size: 14))
.foregroundColor(Color.gray)
Text("2021/12/01")
Text(keyItem.createdDateStr)
.font(.system(size: 14))
.foregroundColor(Color.gray)
}
@ -42,6 +91,15 @@ struct KeyItemView: View {
HStack {
Button("重置") {
print("点击重置")
Task {
do {
_ = try await HttpRequest.regenKey(id: keyItem.id)
HttpRequest.loadKeys()
HToast.showSuccess("已重置")
} catch {
}
}
}
.font(.system(size: 20))
.frame(width: 90, height: 42)
@ -53,6 +111,7 @@ struct KeyItemView: View {
Button("复制") {
print("点击复制")
UIPasteboard.general.string = keyItem.key
HToast.showSuccess("已复制")
}
.font(.system(size: 20))
.frame(width: 90, height: 42)
@ -67,6 +126,6 @@ struct KeyItemView: View {
struct KeyItemView_Previews: PreviewProvider {
static var previews: some View {
KeyItemView(keyItem: KeyItem(id: 1, key: "Key"))
KeyItemView(keyItem: KeyItem(id: 1, name: "name", uid: "1", key: "Key", created_at: "1111"))
}
}

View File

@ -9,26 +9,29 @@ import SwiftUI
/// Key
struct KeyListView: View {
@State private var keyItems = [
KeyItem(id: 1, key: UUID().uuidString),
KeyItem(id: 2, key: UUID().uuidString),
KeyItem(id: 3, key: UUID().uuidString),
KeyItem(id: 4, key: UUID().uuidString),
]
@EnvironmentObject private var store: AppState
var body: some View {
BaseNavigationView(title: "Key") {
ScrollView {
LazyVStack(alignment: .center) {
ForEach(keyItems) { keyItem in
ForEach(store.keys.reversed()) { keyItem in
DeletableView(contentView: {
CardView {
KeyItemView(keyItem: keyItem)
}
}, deleteAction: {
keyItems.removeAll { _keyItem in
store.keys.removeAll { _keyItem in
keyItem.id == _keyItem.id
}
HToast.showSuccess("已删除")
Task {
do {
_ = try await HttpRequest.rmKey(id: keyItem.id)
} catch {
}
}
})
.padding(EdgeInsets(top: 18, leading: 26, bottom: 0, trailing: 24))
}
@ -36,20 +39,26 @@ struct KeyListView: View {
}
}
.navigationBarItems(trailing: Button(action: {
let keyItem = KeyItem(id: Int(arc4random_uniform(1000)), key: UUID().uuidString)
withAnimation(.easeOut) {
keyItems.insert(keyItem, at: 0)
Task {
let keys = try await HttpRequest.genKey().keys
withAnimation(.easeOut) {
store.keys = keys
}
HToast.showSuccess("已添加新Key")
}
}, label: {
Image(systemName: "plus")
.foregroundColor(Color(UIColor.lightGray))
}))
}
.onAppear {
HttpRequest.loadKeys()
}
}
}
struct KeyView_Previews: PreviewProvider {
static var previews: some View {
KeyListView()
KeyListView().environmentObject(AppState.shared)
}
}

View File

@ -10,6 +10,10 @@ import AuthenticationServices
///
struct LoginView: View {
@EnvironmentObject private var store: AppState
@State private var showLoading = false
var body: some View {
VStack{
Spacer()
@ -17,40 +21,28 @@ struct LoginView: View {
.resizable()
.scaledToFit()
Spacer()
AppleSignInButton(
onRequest: { request in
request.requestedScopes = [.fullName, .email]
},
onCompletion: { result in
switch result {
case let .success(authorization):
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
// IDAPP
print(appleIDCredential.user) // 000791.7a323f1326dd4674bc16d32fd6339875.1424
// emailfullName->Apple ID->->使AppleIDApp APP
print(appleIDCredential.email as Any) // easychen@qq.com
print(appleIDCredential.fullName as Any) // givenName: lijie familyName: chen
// JWTtoken.3, base64
let idToken = String(data:appleIDCredential.identityToken!, encoding: .utf8)
print(idToken as Any)
Task {
do {
let result = try await HttpRequest.login(idToken: idToken!)
print(result)
} catch {
print(error)
}
}
if showLoading {
ProgressView()
.scaleEffect(1.5)
.frame(height: 64)
} else {
AppleSignInButton(
onRequest: { request in
request.requestedScopes = [.fullName, .email]
},
onCompletion: { result in
do {
showLoading = true
store.token = try await store.appleIdLogin(result).token
//
} catch {
showLoading = false
}
case let .failure(error):
print(error)
}
}
)
.frame(maxWidth: 375, minHeight: 64, maxHeight: 64)
.padding()
)
.frame(maxWidth: 375, minHeight: 64, maxHeight: 64)
.padding()
}
Spacer()
}
.padding()

View File

@ -9,32 +9,38 @@ import SwiftUI
/// APP
struct MainView: View {
@EnvironmentObject private var store: AppState
var body: some View {
TabView {
TabView.init(selection: $store.tabSelectedIndex) {
DeviceListView()
.tabItem {
Label("设备",systemImage: "ipad.and.iphone")
}
.tag(0)
KeyListView()
.tabItem{
Label("Key",systemImage: "key")
}
.tag(1)
MessageListView()
.tabItem({Label("消息",systemImage: "message")}).onTapGesture {
}
.tag(2)
SettingsView()
.tabItem{
Label("设置",systemImage: "gearshape")
}
.tag(3)
}
}
}
struct MainView_Previews: PreviewProvider {
static var previews: some View {
MainView()
MainView().environmentObject(AppState.shared)
}
}

View File

@ -6,8 +6,12 @@
//
import SwiftUI
import MarkdownUI
import SDWebImageSwiftUI
import Photos
struct MessageItemView: View {
let messageItem: MessageItem
///
let deleteAction : () -> ()
@ -23,7 +27,7 @@ struct MessageItemView: View {
Text("key名字")
.font(.system(size: 14))
.foregroundColor(Color(UIColor.darkGray))
Text("· 5分钟前")
Text(messageItem.createdDateStr)
.font(.system(size: 12))
.foregroundColor(Color(UIColor.darkGray))
HLine().stroke(Color(UIColor.lightGray))
@ -31,33 +35,122 @@ struct MessageItemView: View {
}
DeletableView(contentView: {
CardView {
HStack{
Text("纯文本的效果")
.font(.system(size: 14))
.foregroundColor(Color(UIColor.darkGray))
.padding()
Spacer(minLength: 0)
}
.contextMenu {
Button("复制") {
UIPasteboard.general.string = "someText"
}
}
}
MessageContentView(messageItem: messageItem)
}, deleteAction: deleteAction)
.padding(EdgeInsets(top: 10, leading: 26, bottom: 0, trailing: 24))
.padding(messageItem.type == "image" ? EdgeInsets.init() : EdgeInsets(top: 10, leading: 26, bottom: 0, trailing: 24))
}
.padding(.top, 25)
}
}
struct MessageContentView: View {
let messageItem: MessageItem
@State private var image: PlatformImage? = nil
var body: some View {
switch messageItem.type {
case "markdown":
CardView {
VStack(alignment: .leading, spacing: 5) {
Markdown(Document(messageItem.text))
.markdownStyle(
DefaultMarkdownStyle(
font: .system(size: 14),
foregroundColor: UIColor.darkGray
)
)
if !messageItem.desp.isEmpty {
Markdown(Document(messageItem.desp))
.markdownStyle(
DefaultMarkdownStyle(
font: .system(size: 14),
foregroundColor: UIColor.darkGray
)
)
}
}
.padding()
}
case "image":
WebImage(url: URL(string: messageItem.text))
.onSuccess { image, data, cacheType in
self.image = image
}
.resizable()
.scaledToFill()
.contextMenu {
Button {
UIPasteboard.general.image = image
HToast.showSuccess("已拷贝")
} label: {
Label("拷贝图片",systemImage: "doc.on.doc")
}
Button {
guard let image = image else { return }
PHPhotoLibrary.shared().performChanges {
PHAssetChangeRequest.creationRequestForAsset(from: image)
} completionHandler: { (isSuccess, error) in
DispatchQueue.main.async {
if isSuccess {//
print("Success")
HToast.showSuccess("保存成功")
} else {
print(error as Any)
HToast.showError("保存失败")
}
}
}
} label: {
Label("保存图片",systemImage: "square.and.arrow.down")
}
}
default:
CardView {
VStack(alignment: .leading, spacing: 5) {
HStack{
Text(messageItem.text)
.font(.system(size: 14))
.foregroundColor(Color(UIColor.darkGray))
Spacer(minLength: 0)
}
if !messageItem.desp.isEmpty {
Text(messageItem.desp)
.font(.system(size: 14))
.foregroundColor(Color(UIColor.darkGray))
}
}
.padding()
.contextMenu {
Button {
UIPasteboard.general.string = messageItem.text + messageItem.desp
HToast.showSuccess("已复制")
} label: {
Label("复制",systemImage: "doc.on.doc")
}
}
}
}
}
}
struct MessageItemView_Previews: PreviewProvider {
static var previews: some View {
VStack {
MessageItemView(){}
MessageItemView(){}
MessageItemView(messageItem: MessageItem(id: 1, uid: "1", text: "纯文本的效果", desp: "你好呀", type: "text", created_at: "2022-01-08T18:00:48.000000Z")){}
MessageItemView(messageItem: MessageItem(id: 1, uid: "1", text: "纯文本的效果纯文本的效果纯文本的效果纯文本的效果纯文本的效果纯文本的效果纯文本的效果", desp: "", type: "text", created_at: "2022-01-08T18:00:48.000000Z")){}
MessageItemView(messageItem: MessageItem(id: 1, uid: "1", text: "https://blog.wskfz.com/usr/uploads/2018/06/2498727457.png", desp: "", type: "image", created_at: "2022-01-08T18:00:48.000000Z")){}
MessageItemView(messageItem: MessageItem(id: 1, uid: "1", text: "https://blog.wskfz.com/usr/uploads/2018/06/2151130181.png", desp: "", type: "image", created_at: "2022-01-08T18:00:48.000000Z")){}
MessageItemView(messageItem: MessageItem(id: 1, uid: "1", text: "https://blog.wskfz.com/usr/uploads/2018/06/1718629805.png", desp: "", type: "image", created_at: "2022-01-08T18:00:48.000000Z")){}
MessageItemView(messageItem: MessageItem(id: 1, uid: "1", text: "*MarkDown*的**效果**", desp: "*MarkDown*的**效果**", type: "markdown", created_at: "2021-12-28T13:44:48.000000Z")){}
MessageItemView(messageItem: MessageItem(id: 1, uid: "1", text: """
It's very easy to make some words **bold** and other words *italic* with Markdown.
**Want to experiment with Markdown?** Play with the [reference CommonMark
implementation](https://spec.commonmark.org/dingus/).
""", desp: "", type: "markdown", created_at: "2021-12-28T13:44:48.000000Z")){}
Spacer()
}
}

View File

@ -9,21 +9,27 @@ import SwiftUI
///
struct MessageListView: View {
@State private var messages = Array(0..<10)
@State private var isShowTest = true
@EnvironmentObject private var store: AppState
var body: some View {
BaseNavigationView(title: "消息") {
ScrollView {
LazyVStack(alignment: .leading) {
if isShowTest {
if store.isShowTestPush {
TestPushView()
}
ForEach(messages, id: \.self) { msg in
MessageItemView {
messages.removeAll { _msg in
_msg == msg
ForEach(store.messages) { messageItem in
MessageItemView(messageItem: messageItem) {
store.messages.removeAll { _messageItem in
_messageItem.id == messageItem.id
}
HToast.showSuccess("已删除")
Task {
do {
_ = try await HttpRequest.rmMessage(id: messageItem.id)
} catch {
}
}
}
}
@ -32,17 +38,23 @@ struct MessageListView: View {
}
.navigationBarItems(trailing: Button(action: {
withAnimation(.easeOut) {
isShowTest = !isShowTest
store.isShowTestPush = !store.isShowTestPush
}
}, label: {
Image(systemName: isShowTest ? "chevron.up" : "chevron.down")
Image(systemName: store.isShowTestPush ? "chevron.up" : "chevron.down")
.foregroundColor(Color(UIColor.lightGray))
}))
}
.onAppear {
Task {
store.messages = try await HttpRequest.getMessages().messages
}
}
}
}
struct TestPushView: View {
@EnvironmentObject private var store: AppState
@State private var testText = ""
var body: some View {
TextEditor(text: $testText)
@ -52,6 +64,26 @@ struct TestPushView: View {
Button("推送测试") {
print("点击推送测试")
if testText.isEmpty {
HToast.showError("推送失败, 请先输入推送内容")
return
}
Task {
if store.keys.isEmpty {
store.keys = try await HttpRequest.getKeys().keys
}
if let keyItem = store.keys.first {
_ = try await HttpRequest.push(pushkey: keyItem.key, text: testText, desp: "", type: "")
testText = ""
HToast.showSuccess("推送成功")
let messages = try await HttpRequest.getMessages().messages
withAnimation(.easeOut) {
store.messages = messages
}
} else {
HToast.showError("推送失败, 请先添加一个Key")
}
}
}
.font(.system(size: 20))
.frame(width: 104, height: 42)

View File

@ -18,8 +18,8 @@ struct SettingsItemView: View {
Text(title)
.font(.system(size: 18))
.foregroundColor(Color(UIColor.darkGray))
.padding()
Spacer()
.padding(.leading, 16)
Spacer(minLength: 0)
Button(button) {
print("点击\(button)")
action()

View File

@ -6,32 +6,49 @@
//
import SwiftUI
//import StoreKit
///
struct SettingsView: View {
@EnvironmentObject private var store: AppState
var body: some View {
BaseNavigationView(title: "设置") {
VStack {
SettingsItemView(title: "登录为 Hext", button: "退出") {
SettingsItemView(title: "登录为 \(store.userInfo?.name ?? "--")", button: "退出") {
store.token = ""
}
.padding(EdgeInsets(top: 18, leading: 20, bottom: 0, trailing: 20))
SettingsItemView(title: "自定义服务器", button: "扫码") {
}
.disabled(true)
.padding(EdgeInsets(top: 18, leading: 20, bottom: 0, trailing: 20))
SettingsItemView(title: "喜欢PushDeer?", button: "评分") {
let urlStr = "itms-apps://itunes.apple.com/app/id\(1596771139)?action=write-review"
UIApplication.shared.open(URL(string: urlStr)!, options: [:], completionHandler: nil)
// , 3,
// SKStoreReviewController.requestReview()
}
.padding(EdgeInsets(top: 18, leading: 20, bottom: 0, trailing: 20))
Spacer()
}
}
.onAppear {
if store.userInfo != nil {
return
}
Task {
store.userInfo = try await HttpRequest.getUserInfo()
}
}
}
}
struct SettingsView_Previews: PreviewProvider {
static var previews: some View {
SettingsView()
SettingsView().environmentObject(AppState.shared)
}
}

View File

@ -1,20 +0,0 @@
//
// ContentView.swift
// PushDeerClip
//
// Created by HEXT on 2021/12/30.
//
import SwiftUI
struct ContentView: View {
var body: some View {
MainView()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

View File

@ -2,11 +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>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSAppClip</key>
<dict>
<key>NSAppClipRequestEphemeralUserNotification</key>
@ -14,5 +9,20 @@
<key>NSAppClipRequestLocationConfirmation</key>
<false/>
</dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSBonjourServices</key>
<array>
<string>_adhp._tcp</string>
</array>
<key>NSLocalNetworkUsageDescription</key>
<string>APP需要访问本地网络以供调试(仅在开发时)</string>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>APP为您提供保存图片到相册的功能</string>
</dict>
</plist>