支持微信登录, 修复一些bug:

支持微信登录, 支持同时绑定微信和苹果账号;
解决markdown标题文字显示不全的问题;
markdown中的链接支持内嵌浏览器打开;
增加是否使用内置浏览器的设置项;
改名输入框, 失去焦点的时候也保存;
This commit is contained in:
hext 2022-02-27 23:02:58 +08:00
parent 2310265c77
commit 2136883536
29 changed files with 1100 additions and 43 deletions

View File

@ -1,6 +1,8 @@
# Uncomment the next line to define a global platform for your project
platform :ios, '14.0'
load 'remove_unsupported_libraries.rb'
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
@ -10,14 +12,14 @@ def commonPods
pod 'SDWebImageSwiftUI', '~> 2.0.2'
pod 'KRProgressHUD', '~> 3.4.7'
pod 'IQKeyboardManagerSwift', '~> 6.5.9'
# pod 'WoodPeckeriOS', :configurations => ['Debug']
end
target 'PushDeer' do
commonPods
# Pods for PushDeer
# PushDeer 独享的依赖, Clip 不支持
pod 'WechatOpenSDK', '~> 1.8.7.1'
pod 'WoodPeckeriOS', :configurations => ['Debug']
end
target 'PushDeerClip' do
@ -25,3 +27,20 @@ target 'PushDeerClip' do
# Pods for PushDeerClip
end
# define unsupported pods
def unsupported_pods
# macCatalyst 不支持的库
['WoodPeckeriOS', 'WechatOpenSDK']
end
def supported_pods
# macCatalyst 支持的库
['Moya', 'SDWebImageSwiftUI', 'KRProgressHUD', 'IQKeyboardManagerSwift']
end
# install all pods except unsupported ones
post_install do |installer|
$verbose = false # remove or set to false to avoid printing
installer.configure_support_catalyst(supported_pods, unsupported_pods)
end

View File

@ -13,12 +13,16 @@ PODS:
- SDWebImage/Core (5.12.1)
- SDWebImageSwiftUI (2.0.2):
- SDWebImage (~> 5.10)
- WechatOpenSDK (1.8.7.1)
- WoodPeckeriOS (1.2.93)
DEPENDENCIES:
- IQKeyboardManagerSwift (~> 6.5.9)
- KRProgressHUD (~> 3.4.7)
- Moya (~> 15.0)
- SDWebImageSwiftUI (~> 2.0.2)
- WechatOpenSDK (~> 1.8.7.1)
- WoodPeckeriOS
SPEC REPOS:
trunk:
@ -29,6 +33,8 @@ SPEC REPOS:
- Moya
- SDWebImage
- SDWebImageSwiftUI
- WechatOpenSDK
- WoodPeckeriOS
SPEC CHECKSUMS:
Alamofire: 1c4fb5369c3fe93d2857c780d8bbe09f06f97e7c
@ -38,7 +44,9 @@ SPEC CHECKSUMS:
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
SDWebImage: 4dc3e42d9ec0c1028b960a33ac6b637bb432207b
SDWebImageSwiftUI: 8a3923c95108312b03a599ec1498754af55a6819
WechatOpenSDK: 6a4d1436c15b3b5fe2a0bd383f3046010186da44
WoodPeckeriOS: 12ec7f38c695e51cd94a476228888dfe85d9d916
PODFILE CHECKSUM: 3c8d668f811e3c29bb70fc8a24d2ecd9c266ba45
PODFILE CHECKSUM: 1b349626994062a8291e3db07d3dbf087894c4d2
COCOAPODS: 1.11.2

View File

@ -77,9 +77,13 @@
52EB90AE2778AFD60048E0ED /* BaseNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB90AD2778AFD60048E0ED /* BaseNavigationView.swift */; };
52EB90B02778D67F0048E0ED /* KeyItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB90AF2778D67F0048E0ED /* KeyItemView.swift */; };
52EB90B32778DA4E0048E0ED /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB90B22778DA4E0048E0ED /* Line.swift */; };
52EED71E27C9394D0086A804 /* WXDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EED71D27C9394D0086A804 /* WXDelegate.swift */; };
52EED71F27C93B960086A804 /* WXDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EED71D27C9394D0086A804 /* WXDelegate.swift */; };
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 */; };
52FB1FEC27CA9D7300367DE0 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52FB1FEB27CA9D7300367DE0 /* SafariView.swift */; };
52FB1FED27CA9D7300367DE0 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52FB1FEB27CA9D7300367DE0 /* SafariView.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 */
@ -143,6 +147,7 @@
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>"; };
52B8CF70277E0B46004CB680 /* PushDeerClip.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PushDeerClip.entitlements; sourceTree = "<group>"; };
52BE373227C236DD004AA630 /* PushDeer-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PushDeer-Bridging-Header.h"; sourceTree = "<group>"; };
52E317D8279305BB000B8BB1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
52E317DB279305BB000B8BB1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
52E317DE279305BB000B8BB1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@ -153,10 +158,12 @@
52EB90AD2778AFD60048E0ED /* BaseNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseNavigationView.swift; sourceTree = "<group>"; };
52EB90AF2778D67F0048E0ED /* KeyItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyItemView.swift; sourceTree = "<group>"; };
52EB90B22778DA4E0048E0ED /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = "<group>"; };
52EED71D27C9394D0086A804 /* WXDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WXDelegate.swift; sourceTree = "<group>"; };
52F0243C277733CE0071D861 /* PushDeer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PushDeer.entitlements; sourceTree = "<group>"; };
52F0243E277737470071D861 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
52F2C222277961D7006F08DC /* SettingsItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsItemView.swift; sourceTree = "<group>"; };
52F40D2E277CA05600766C24 /* MessageItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageItemView.swift; sourceTree = "<group>"; };
52FB1FEB27CA9D7300367DE0 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
69F56B2711ED98819D474BE3 /* Pods-PushDeerClip.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerClip.debug.xcconfig"; path = "Target Support Files/Pods-PushDeerClip/Pods-PushDeerClip.debug.xcconfig"; sourceTree = "<group>"; };
8B9D658D778AE868A0E052A8 /* Pods-PushDeer.debug-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeer.debug-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeer/Pods-PushDeer.debug-selfhosted.xcconfig"; sourceTree = "<group>"; };
9CC775BE0326BF31C6FACF06 /* Pods-PushDeerClip.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerClip.release.xcconfig"; path = "Target Support Files/Pods-PushDeerClip/Pods-PushDeerClip.release.xcconfig"; sourceTree = "<group>"; };
@ -213,6 +220,7 @@
52450F3A278491F8003652D8 /* AppState.swift */,
52450F412784943F003652D8 /* HttpRequest.swift */,
526A1E722791E03900BA2177 /* Persistence.swift */,
52EED71D27C9394D0086A804 /* WXDelegate.swift */,
);
path = Service;
sourceTree = "<group>";
@ -264,6 +272,7 @@
52483FC1277ED6D5003A100E /* AppDelegate.swift */,
5292F4F82776BC7900B9A7BB /* PushDeerApp.swift */,
5292F4FA2776BC7900B9A7BB /* ContentView.swift */,
52BE373227C236DD004AA630 /* PushDeer-Bridging-Header.h */,
5292F4FC2776BC7A00B9A7BB /* Assets.xcassets */,
5292F4FE2776BC7A00B9A7BB /* Preview Content */,
);
@ -285,6 +294,7 @@
52B8CF5E277DE660004CB680 /* AppleSignInButton.swift */,
52AC5C2727B7FE1D00EEB185 /* ViewExtension.swift */,
52AC5C2A27B8206D00EEB185 /* ListTest.swift */,
52FB1FEB27CA9D7300367DE0 /* SafariView.swift */,
);
path = Common;
sourceTree = "<group>";
@ -410,9 +420,11 @@
TargetAttributes = {
5292F4F42776BC7900B9A7BB = {
CreatedOnToolsVersion = 13.2.1;
LastSwiftMigration = 1320;
};
52B8CF63277E0B44004CB680 = {
CreatedOnToolsVersion = 13.2.1;
LastSwiftMigration = 1320;
};
};
};
@ -551,6 +563,7 @@
buildActionMask = 2147483647;
files = (
52F0243F277737470071D861 /* LoginView.swift in Sources */,
52FB1FEC27CA9D7300367DE0 /* SafariView.swift in Sources */,
523150DC2778762B00941EDC /* DeviceItemView.swift in Sources */,
523150D9277875FB00941EDC /* DeletableView.swift in Sources */,
52163EBB277741AC00594190 /* SettingsView.swift in Sources */,
@ -571,6 +584,7 @@
526A1E7D2792B2A600BA2177 /* MessageModel.swift in Sources */,
52483FC2277ED6D5003A100E /* AppDelegate.swift in Sources */,
52450F3827848243003652D8 /* PushDeerApi.swift in Sources */,
52EED71E27C9394D0086A804 /* WXDelegate.swift in Sources */,
52EB90AE2778AFD60048E0ED /* BaseNavigationView.swift in Sources */,
524E99E627B3CD0F00292396 /* EndpointView.swift in Sources */,
52EB90AC2778ADF80048E0ED /* CardView.swift in Sources */,
@ -589,6 +603,7 @@
buildActionMask = 2147483647;
files = (
52B8CF82277E0C06004CB680 /* AppleSignInButton.swift in Sources */,
52FB1FED27CA9D7300367DE0 /* SafariView.swift in Sources */,
52B8CF78277E0BF1004CB680 /* MainView.swift in Sources */,
52B8CF79277E0BFB004CB680 /* DeviceListView.swift in Sources */,
52B8CF84277E0C12004CB680 /* CardView.swift in Sources */,
@ -609,6 +624,7 @@
526A1E7E2792B2A600BA2177 /* MessageModel.swift in Sources */,
52483FC3277ED6D5003A100E /* AppDelegate.swift in Sources */,
52450F3927848243003652D8 /* PushDeerApi.swift in Sources */,
52EED71F27C93B960086A804 /* WXDelegate.swift in Sources */,
52B8CF86277E0C12004CB680 /* BaseNavigationView.swift in Sources */,
524E99E727B3CD0F00292396 /* EndpointView.swift in Sources */,
52FBA09427874879003308C2 /* ContentView.swift in Sources */,
@ -733,6 +749,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-SH";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "PushDeer/PushDeer-SelfHosted.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
@ -760,6 +777,8 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PushDeer/PushDeer-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -771,6 +790,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-SH";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "PushDeerClip/PushDeerClip-SelfHosted.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
@ -801,6 +821,8 @@
PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.self.ios.Clip;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PushDeer/PushDeer-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -870,6 +892,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-SH";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "PushDeer/PushDeer-SelfHosted.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
@ -897,6 +920,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PushDeer/PushDeer-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -908,6 +932,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-SH";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "PushDeerClip/PushDeerClip-SelfHosted.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
@ -938,6 +963,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.self.ios.Clip;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PushDeer/PushDeer-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -1067,6 +1093,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = PushDeer/PushDeer.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
@ -1094,6 +1121,8 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PushDeer/PushDeer-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -1105,6 +1134,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = PushDeer/PushDeer.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
@ -1132,6 +1162,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PushDeer/PushDeer-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -1143,6 +1174,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = PushDeerClip/PushDeerClip.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
@ -1173,6 +1205,8 @@
PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.app.ios.Clip;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PushDeer/PushDeer-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -1184,6 +1218,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = PushDeerClip/PushDeerClip.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
@ -1214,6 +1249,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.app.ios.Clip;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PushDeer/PushDeer-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -1262,8 +1298,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/gonzalezreal/MarkdownUI";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.5.2;
kind = upToNextMinorVersion;
minimumVersion = 1.0.0;
};
};
/* End XCRemoteSwiftPackageReference section */

View File

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
<false/>
<key>PreviewsEnabled</key>
<false/>
</dict>

View File

@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/gonzalezreal/AttributedText",
"state": {
"branch": null,
"revision": "c345033e22d5a1cd0e9fe0ec405cc809a8349586",
"version": "0.3.1"
"revision": "2dc2d7864e0fee4b524a5850d7d7cf9a7eeda0fc",
"version": "1.0.0"
}
},
{
@ -24,8 +24,8 @@
"repositoryURL": "https://github.com/gonzalezreal/MarkdownUI",
"state": {
"branch": null,
"revision": "29d94710545952dd4c724cc2ca901848eef54ded",
"version": "0.5.2"
"revision": "94e07c111f1ef0a9c997d2fadac984e8bb7d7405",
"version": "1.0.0"
}
},
{
@ -33,17 +33,8 @@
"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"
"revision": "f8b8ed0be39d2f4aa00a6e8d3b18a62a94eff8d8",
"version": "4.0.0"
}
},
{
@ -51,8 +42,8 @@
"repositoryURL": "https://github.com/gonzalezreal/SwiftCommonMark",
"state": {
"branch": null,
"revision": "ed60da54305c244d0f77bc8d08495e04161e96a4",
"version": "0.1.2"
"revision": "ed252beaddecce28ea6363f800c773d6169011b8",
"version": "1.0.0"
}
},
{

View File

@ -9,8 +9,9 @@ import UIKit
import UserNotifications
import IQKeyboardManagerSwift
@MainActor
class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
//
@ -35,6 +36,12 @@ class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDele
IQKeyboardManager.shared.enable = false //
IQKeyboardManager.shared.enableAutoToolbar = true //
#if !targetEnvironment(macCatalyst) && !APPCLIP && !SELFHOSTED
// Mac and AppClip and
//
WXApi.registerApp(Env.wxAppid, universalLink: Env.wxUniversalLink)
#endif
return true
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "100",
"green" : "191",
"red" : "31"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.000",
"blue" : "0",
"green" : "0",
"red" : "0"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "100",
"green" : "191",
"red" : "31"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "微信LOGO_轮廓.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,24 @@
//
// SafariView.swift
// PushDeer
//
// Created by HEXT on 2022/2/27.
//
import SwiftUI
import SafariServices
struct SafariView: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: Context) -> SFSafariViewController {
let safariVC = SFSafariViewController.init(url: url)
safariVC.dismissButtonStyle = .close
return safariVC
}
func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {
// update code
}
}

View File

@ -21,4 +21,9 @@ struct Env {
static let onlineApiEndpoint = "https://api2.pushdeer.com"
/// AppStore appId, : 1608017631; 线: 1596771139
static let appStoreId = isSelfHosted ? 1608017631 : 1596771139
/// ID
static let wxAppid = "wx3ae07931d0555a24"
/// Universal Link
static let wxUniversalLink = "https://vip.pushdeer.com/app/"
}

View File

@ -2,8 +2,26 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>weixin</string>
<string>weixinULAPI</string>
</array>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>weixin</string>
<key>CFBundleURLSchemes</key>
<array>
<string>wx3ae07931d0555a24</string>
</array>
</dict>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>

View File

@ -22,9 +22,9 @@ struct TokenContent: Codable{
struct UserInfoContent: Codable{
let id: Int
let name: String
let name: String?
let email: String?
let apple_id: String
let apple_id: String?
let wechat_id: String?
let level: Int
let created_at: String
@ -74,7 +74,7 @@ struct ActionContent: Codable{
let message: String
}
struct PushResultContent: Codable{
struct ResultContent: Codable{
let result: Array<String>
}

View File

@ -0,0 +1,7 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#if __has_include("WXApi.h")
#import "WXApi.h"
#endif

View File

@ -11,6 +11,7 @@
<key>com.apple.developer.associated-domains</key>
<array>
<string>appclips:vip.pushdeer.com</string>
<string>applinks:vip.pushdeer.com</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>

View File

@ -16,6 +16,18 @@ struct PushDeerApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL(perform: { url in
print(#function, url)
#if !targetEnvironment(macCatalyst) && !APPCLIP && !SELFHOSTED
WXApi.handleOpen(url, delegate: WXDelegate.shared)
#endif
})
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb, perform: { userActivity in
print(#function, userActivity.webpageURL as Any)
#if !targetEnvironment(macCatalyst) && !APPCLIP && !SELFHOSTED
WXApi.handleOpenUniversalLink(userActivity, delegate: WXDelegate.shared)
#endif
})
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
// ,
UIApplication.shared.applicationIconBadgeNumber = 0

View File

@ -37,6 +37,12 @@ class AppState: ObservableObject {
UserDefaults.standard.set(isShowTestPush, forKey: "PushDeer_isShowTestPush")
}
}
/// 使
@Published var isUseBuiltInBrowser: Bool {
didSet {
UserDefaults.standard.set(isUseBuiltInBrowser, forKey: "PushDeer_isUseBuiltInBrowser")
}
}
/// API endpoint
@Published var api_endpoint : String {
@ -59,10 +65,12 @@ class AppState: ObservableObject {
let _token = UserDefaults.standard.string(forKey: "PushDeer_token")
let _tabSelectedIndex = UserDefaults.standard.integer(forKey: "PushDeer_tabSelectedIndex")
let _isShowTestPush = UserDefaults.standard.object(forKey: "PushDeer_isShowTestPush")
let _isUseBuiltInBrowser = UserDefaults.standard.object(forKey: "PushDeer_isUseBuiltInBrowser")
let _api_endpoint = UserDefaults.standard.string(forKey: "PushDeer_api_endpoint")
token = _token ?? ""
tabSelectedIndex = _tabSelectedIndex
isShowTestPush = _isShowTestPush as? Bool ?? true
isUseBuiltInBrowser = _isUseBuiltInBrowser as? Bool ?? true
api_endpoint = _api_endpoint ?? ""
}
@ -97,6 +105,10 @@ class AppState: ObservableObject {
}
case let .failure(error):
print(error)
if (error as NSError).code == 1001 {
// Apple
throw NSError(domain: NSLocalizedString("登录失败", comment: "AppleId登录失败时提示") + "\n\(error.localizedDescription)", code: 1001, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("你已取消授权", comment: "")])
}
// Apple
throw NSError(domain: NSLocalizedString("登录失败", comment: "AppleId登录失败时提示") + "\n\(error.localizedDescription)", code: -2, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("登录失败", comment: "AppleId登录失败时提示") + "(-2)\n\(error.localizedDescription)"])
}

View File

@ -20,6 +20,12 @@ struct HttpRequest {
switch result {
case let .success(response):
do {
print(response)
if response.statusCode != 200 {
continuation.resume(throwing: NSError(domain: NSLocalizedString("接口报错", comment: "接口报错时提示"), code: response.statusCode, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("接口报错", comment: "接口报错时提示") + "(\(response.statusCode)"]))
return
}
print((try? response.mapJSON()) ?? "返回值解析失败")
let result = try JSONDecoder().decode(ApiResult<T>.self, from: response.data)
print(result)
if let content = result.content, result.code == 0 {
@ -50,6 +56,19 @@ struct HttpRequest {
return try await request(.login(idToken: idToken), resultType: TokenContent.self)
}
static func wechatLogin(code: String) async throws -> TokenContent {
return try await request(.wechatLogin(code: code), resultType: TokenContent.self)
}
///
/// | | |
/// | - | - |
/// | type | apple wechat |
/// | tokenorcode | type apple idToken code |
static func mergeUser(type: String, tokenorcode: String) async throws -> ResultContent {
return try await request(.mergeUser(token: AppState.shared.token, type: type, tokenorcode: tokenorcode), resultType: ResultContent.self)
}
static func getUserInfo() async throws -> UserInfoContent {
return try await request(.getUserInfo(token: AppState.shared.token), resultType: UserInfoContent.self)
}
@ -105,8 +124,8 @@ struct HttpRequest {
}
}
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 push(pushkey: String, text: String, desp: String, type: String) async throws -> ResultContent {
return try await request(.push(pushkey: pushkey, text: text, desp: desp, type: type), resultType: ResultContent.self)
}
static func getMessages() async throws -> MessageContent {

View File

@ -11,7 +11,17 @@ import Moya
enum PushDeerApi {
case fake
/// idToken
case login(idToken: String)
/// oauth code
case wechatLogin(code: String)
///
/// | | |
/// | - | - |
/// | token | token |
/// | type | apple wechat |
/// | tokenorcode | type apple idToken code |
case mergeUser(token: String, type: String, tokenorcode: String)
case getUserInfo(token: String)
case regDevice(token: String, name: String, device_id: String, is_clip: Int)
@ -47,6 +57,10 @@ extension PushDeerApi: TargetType {
return "/login/fake"
case .login:
return "/login/idtoken"
case .wechatLogin:
return "/login/wecode"
case .mergeUser:
return "/user/merge"
case .getUserInfo:
return "/user/info"
@ -91,6 +105,10 @@ extension PushDeerApi: TargetType {
return .requestParameters(parameters: [:], encoding: URLEncoding.queryString)
case let .login(idToken):
return .requestParameters(parameters: ["idToken": idToken], encoding: URLEncoding.queryString)
case let .wechatLogin(code):
return .requestParameters(parameters: ["code": code], encoding: URLEncoding.queryString)
case let .mergeUser(token, type, tokenorcode):
return .requestParameters(parameters: ["token": token, "type": type, "tokenorcode": tokenorcode], encoding: URLEncoding.queryString)
case let .getUserInfo(token):
return .requestParameters(parameters: ["token": token], encoding: URLEncoding.queryString)

View File

@ -0,0 +1,57 @@
//
// WXDelegate.swift
// PushDeer
//
// Created by HEXT on 2022/2/26.
//
import Foundation
#if !targetEnvironment(macCatalyst) && !APPCLIP && !SELFHOSTED
@MainActor
class WXDelegate: NSObject, WXApiDelegate {
static let shared = WXDelegate()
private override init() { super.init() }
func onReq(_ req: BaseReq) {
print(#function, req.type, req.openID)
}
func onResp(_ resp: BaseResp) {
print(#function, resp.type, resp.errCode, resp.errStr)
if let resp = resp as? SendAuthResp { //
print(resp.code as Any, resp.state as Any, resp.lang as Any, resp.country as Any)
switch resp.errCode {
case 0: //
if let code = resp.code, let state = resp.state {
Task {
do {
if state == "login" {
AppState.shared.token = try await HttpRequest.wechatLogin(code: code).token
// AppState token , SwiftUI ContentView
} else if state == "bind" {
_ = try await HttpRequest.mergeUser(type: "wechat", tokenorcode: code)
// ,
AppState.shared.userInfo = try await HttpRequest.getUserInfo()
}
} catch {
HToast.showError(error.localizedDescription)
}
}
}
break
case -2: //
HToast.showWarning(NSLocalizedString("你已取消授权", comment: ""))
break
case -4: //
HToast.showError(NSLocalizedString("你已拒绝授权", comment: ""))
break
default:
break
}
}
}
}
#endif

View File

@ -15,9 +15,16 @@ struct EditableText: View {
}
var body: some View {
TextField(placeholder, text: $value, onCommit: {
print("修改文本:", value)
self.onCommit(value)
TextField(placeholder, text: $value, onEditingChanged: { focus in
print("focus", focus, placeholder, value)
if !focus {
//
self.onCommit(value)
}
}, onCommit: {
// enter, 使
// print(":", value)
// self.onCommit(value)
})
.font(.system(size: 20))
.foregroundColor(Color.accentColor)

View File

@ -9,7 +9,7 @@ import SwiftUI
/// View
struct DeviceItemView: View {
let deviceItem: DeviceItem
@State var deviceItem: DeviceItem
@EnvironmentObject private var store: AppState
var body: some View {
@ -30,10 +30,14 @@ struct DeviceItemView: View {
.padding(EdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 8))
EditableText(placeholder: NSLocalizedString("输入设备名称", comment: ""), value: deviceItem.name) { value in
if deviceItem.name == value {
return
}
Task {
//
_ = try await HttpRequest.renameDevice(id: deviceItem.id, name: value)
HToast.showSuccess(NSLocalizedString("已修改设备名称", comment: ""))
deviceItem.name = value
// Item
let index = store.devices.firstIndex { $0.id == deviceItem.id }
if let index = index {
@ -44,7 +48,7 @@ struct DeviceItemView: View {
}
Text(getInfo(deviceItem: deviceItem))
.font(.system(size: 20))
Spacer()
Spacer(minLength: 12)
}
.frame(height: 80)
}

View File

@ -9,7 +9,7 @@ import SwiftUI
/// Key View
struct KeyItemView: View {
let keyItem: KeyItem
@State var keyItem: KeyItem
@EnvironmentObject private var store: AppState
var body: some View {
@ -20,10 +20,14 @@ struct KeyItemView: View {
.scaledToFit()
.frame(width: 38, height: 38)
EditableText(placeholder: NSLocalizedString("输入key名称", comment: ""), value: keyItem.name) { value in
if keyItem.name == value {
return
}
Task {
//
_ = try await HttpRequest.renameKey(id: keyItem.id, name: value)
HToast.showSuccess(NSLocalizedString("已修改key名称", comment: ""))
keyItem.name = value
// keyItem
let index = store.keys.firstIndex { $0.id == keyItem.id }
if let index = index {

View File

@ -31,6 +31,7 @@ struct LoginView: View {
.scaleEffect(1.5)
.frame(height: 64)
} else {
//
AppleSignInButton(
onRequest: { request in
request.requestedScopes = [.fullName, .email]
@ -42,6 +43,11 @@ struct LoginView: View {
//
} catch {
showLoading = false
if (error as NSError).code == 1001 {
//
HToast.showWarning(error.localizedDescription)
return
}
HToast.showError(error.localizedDescription)
}
}
@ -49,6 +55,37 @@ struct LoginView: View {
.overlay(RoundedRectangle(cornerRadius: 6).stroke(Color.white))
.frame(maxWidth: 375, minHeight: 64, maxHeight: 64)
.padding()
#if !targetEnvironment(macCatalyst) && !APPCLIP && !SELFHOSTED
if WXApi.isWXAppInstalled() {
//
Button {
let req = SendAuthReq()
req.scope = "snsapi_userinfo";
req.state = "login";
WXApi.send(req) { b in
print("WXApi.send:", b)
}
// AppDelegate onResp
} label: {
HStack {
Image("weixin-login")
.resizable()
.renderingMode(.template)
.scaledToFit()
.frame(height:20)
Text("通过微信登录")
}
.font(.system(size: 26, weight: .semibold))
.foregroundColor(Color("weixinFgColor"))
.frame(maxWidth: 375, minHeight: 64, maxHeight: 64)
}
.background(Color("weixinBgColor"))
.cornerRadius(6)
.overlay(RoundedRectangle(cornerRadius: 6).stroke(Color("weixinFgColor")))
.padding()
}
#endif
}
Spacer()
}

View File

@ -44,33 +44,88 @@ struct MessageItemView: View {
}
}
extension URL: Identifiable {
public var id: Self { self }
}
struct MessageContentView: View {
let messageItem: MessageModel
@EnvironmentObject private var store: AppState
@State private var image: PlatformImage? = nil
@State private var showUrl: URL?
@State private var showActionSheet = false
var body: some View {
switch messageItem.type {
case "markdown":
CardView {
VStack(alignment: .leading, spacing: 5) {
Markdown(Document(messageItem.text ?? ""))
Markdown(messageItem.text ?? "")
.markdownStyle(
DefaultMarkdownStyle(
MarkdownStyle(
font: .system(size: 14),
foregroundColor: UIColor(named: "textColor") ?? UIColor.darkGray
foregroundColor: .init("textColor")
)
)
#if !targetEnvironment(macCatalyst)
.onOpenMarkdownLink { url in
if store.isUseBuiltInBrowser {
showUrl = url
} else {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
#endif
if !(messageItem.desp?.isEmpty ?? true) {
Markdown(Document(messageItem.desp!))
Markdown(messageItem.desp ?? "")
.markdownStyle(
DefaultMarkdownStyle(
MarkdownStyle(
font: .system(size: 14),
foregroundColor: UIColor(named: "textColor") ?? UIColor.darkGray
foregroundColor: .init("textColor")
)
)
#if !targetEnvironment(macCatalyst)
.onOpenMarkdownLink { url in
if store.isUseBuiltInBrowser {
showUrl = url
} else {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
#endif
}
}
.padding()
#if targetEnvironment(macCatalyst)
.contextMenu {
Button {
UIPasteboard.general.string = (messageItem.text ?? "") + "\n" + (messageItem.desp ?? "")
HToast.showSuccess(NSLocalizedString("已复制", comment: ""))
} label: {
Label("复制",systemImage: "doc.on.doc")
}
}
#endif
#if !targetEnvironment(macCatalyst)
.onLongPressGesture {
UIImpactFeedbackGenerator().impactOccurred()
showActionSheet = true
}
.actionSheet(isPresented: $showActionSheet) {
ActionSheet(title: Text("复制消息内容"), message: nil, buttons: [
.default(Text("复制"), action: {
UIPasteboard.general.string = (messageItem.text ?? "") + "\n" + (messageItem.desp ?? "")
HToast.showSuccess(NSLocalizedString("已复制", comment: ""))
}),
.cancel()
])
}
#endif
}
.fullScreenCover(item: $showUrl) {
} content: { url in
SafariView(url: url)
}
case "image":
@ -146,7 +201,7 @@ struct MessageContentView: View {
.padding()
.contextMenu {
Button {
UIPasteboard.general.string = (messageItem.text ?? "") + (messageItem.desp ?? "")
UIPasteboard.general.string = (messageItem.text ?? "") + "\n" + (messageItem.desp ?? "")
HToast.showSuccess(NSLocalizedString("已复制", comment: ""))
} label: {
Label("复制",systemImage: "doc.on.doc")

View File

@ -6,6 +6,7 @@
//
import SwiftUI
import AuthenticationServices
//import StoreKit
///
@ -21,6 +22,14 @@ struct SettingsView: View {
}
.padding(EdgeInsets(top: 18, leading: 20, bottom: 0, trailing: 20))
#if !targetEnvironment(macCatalyst) && !APPCLIP && !SELFHOSTED
if WXApi.isWXAppInstalled() {
LoginInfoView()
.zIndex(-1)
.padding(EdgeInsets(top: -30, leading: 20, bottom: 0, trailing: 20))
}
#endif
if Env.isSelfHosted {
SettingsItemView(title: NSLocalizedString("API endpoint", comment: ""), button: NSLocalizedString("重置", comment: "")) {
store.api_endpoint = ""
@ -37,6 +46,22 @@ struct SettingsView: View {
}
.padding(EdgeInsets(top: 18, leading: 20, bottom: 0, trailing: 20))
#if !targetEnvironment(macCatalyst)
CardView {
HStack{
Toggle(isOn: $store.isUseBuiltInBrowser) {
Text("使用内置浏览器打开链接")
.font(.system(size: 18))
.foregroundColor(Color("textColor"))
}
.padding(16)
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
.frame(height: 74)
}
.padding(EdgeInsets(top: 18, leading: 20, bottom: 0, trailing: 20))
#endif
Spacer()
}
}
@ -64,6 +89,122 @@ struct SettingsView: View {
}
}
struct LoginInfoView: View {
enum AlertType : Identifiable {
var id: Self { self }
case apple
case wechat
}
@EnvironmentObject private var store: AppState
@State private var alertType: AlertType? = nil
static private var coordinator: AppleSignInCoordinator? = nil
var body: some View {
CardView {
HStack(spacing: 16) {
Spacer()
Circle()
.frame(width: 10, height: 10, alignment: .center)
.foregroundColor(store.userInfo?.apple_id?.isEmpty ?? true ? .gray : .green)
Button {
if store.userInfo?.apple_id?.isEmpty ?? true {
alertType = .apple
} else {
HToast.showText(NSLocalizedString("当前已经绑定苹果账号", comment: ""))
}
} label: {
Image(systemName: "applelogo")
.resizable()
.scaledToFit()
.frame( height: 40, alignment: .center)
}
Spacer()
Circle()
.frame(width: 10, height: 10, alignment: .center)
.foregroundColor(store.userInfo?.wechat_id?.isEmpty ?? true ? .gray : .green)
Button {
if store.userInfo?.wechat_id?.isEmpty ?? true {
alertType = .wechat
} else {
HToast.showText(NSLocalizedString("当前已经绑定微信账号", comment: ""))
}
} label: {
Image("weixin-login")
.resizable()
.renderingMode(.template)
.scaledToFit()
.frame(height: 37, alignment: .center)
}
Spacer()
}
.padding(EdgeInsets(top: 32, leading: 0, bottom: 12, trailing: 0))
}
.alert(item: $alertType) { alertType in
var message = ""
switch alertType {
case .apple:
message = NSLocalizedString("准备绑定苹果账号, 如果你绑定的账号之前已经存在, 则会合并到当前账号. (之前的Key可能会被删除)", comment: "")
case .wechat:
message = NSLocalizedString("准备绑定微信账号, 如果你绑定的账号之前已经存在, 则会合并到当前账号. (之前的Key可能会被删除)", comment: "")
}
return Alert(
title: Text("温馨提示"),
message: Text(message),
primaryButton: .default(
Text("绑定"),
action: {
switch alertType {
case .apple:
LoginInfoView.coordinator = AppleSignInCoordinator(
onRequest: { request in
request.requestedScopes = [.fullName, .email]
},
onCompletion: { result in
do {
switch result {
case let .success(authorization):
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
let idToken = String(data:appleIDCredential.identityToken!, encoding: .utf8)
print(idToken as Any)
//
let result = try await HttpRequest.mergeUser(type: "apple", tokenorcode: idToken!)
print(result)
// ,
store.userInfo = try await HttpRequest.getUserInfo()
}
case let .failure(error):
if (error as NSError).code == 1001 {
HToast.showWarning(NSLocalizedString("你已取消授权", comment: ""))
} else {
HToast.showError(error.localizedDescription)
}
}
} catch {
HToast.showError(error.localizedDescription)
}
}
)
LoginInfoView.coordinator?.performRequests()
case .wechat:
#if !targetEnvironment(macCatalyst) && !APPCLIP && !SELFHOSTED
let req = SendAuthReq()
req.scope = "snsapi_userinfo";
req.state = "bind";
WXApi.send(req) { b in
print("WXApi.send:", b)
}
// AppDelegate onResp
#endif
}
}
),
secondaryButton: .cancel(Text("稍后"))
)
}
}
}
struct SettingsView_Previews: PreviewProvider {
static var previews: some View {
SettingsView().environmentObject(AppState.shared)

View File

@ -1,6 +1,9 @@
/* 在设备列表中标识当前设备 */
"(当前设备)" = "(current device)";
/* No comment provided by engineer. */
"绑定" = "Bind";
/* No comment provided by engineer. */
"保存" = "Save";
@ -16,6 +19,12 @@
/* No comment provided by engineer. */
"标题" = "Title";
/* No comment provided by engineer. */
"当前已经绑定苹果账号" = "Apple account is currently bound";
/* No comment provided by engineer. */
"当前已经绑定微信账号" = "WeChat account is currently bound";
/* token失效时提示 */
"登录过期" = "Login expired";
@ -46,6 +55,12 @@
/* No comment provided by engineer. */
"你还未注册当前设备, 注册后才能收到推送, 是否现在注册? (你还可以稍后点击右上角 + 符号添加当前设备)" = "You have not registered the current device, you can receive push notifications after registration, do you want to register now? (You can also click the + symbol in the upper right corner to add the current device later)";
/* No comment provided by engineer. */
"你已拒绝授权" = "You have denied authorization";
/* No comment provided by engineer. */
"你已取消授权" = "You have deauthorized";
/* No comment provided by engineer. */
"您在此应用中的数据都将发送到 endpoint 指向的服务器" = "Your data in this app will be sent to the server pointed to by endpoint";
@ -79,6 +94,9 @@
/* No comment provided by engineer. */
"设置" = "Settings";
/* No comment provided by engineer. */
"使用内置浏览器打开链接" = "Open links using built-in browser";
/* No comment provided by engineer. */
"输入设备名称" = "Enter device name";
@ -86,7 +104,7 @@
"输入key名称" = "Enter key name";
/* No comment provided by engineer. */
"温馨提示" = "Tips";
"通过微信登录" = "Sign in with WeChat";
/* No comment provided by engineer. */
"图片未加载成功" = "Image did not load successfully";
@ -106,6 +124,9 @@
/* 退出登录按钮上的文字 */
"退出" = "Logout";
/* No comment provided by engineer. */
"温馨提示" = "Tips";
/* No comment provided by engineer. */
"喜欢PushDeer?" = "Like PushDeer?";
@ -148,6 +169,12 @@
/* No comment provided by engineer. */
"注册" = "Register";
/* No comment provided by engineer. */
"准备绑定苹果账号, 如果你绑定的账号之前已经存在, 则会合并到当前账号. (之前的Key可能会被删除)" = "Prepare to bind the Apple account. If the account you bind already exists, it will be merged into the current account. (The previous Key may be deleted)";
/* No comment provided by engineer. */
"准备绑定微信账号, 如果你绑定的账号之前已经存在, 则会合并到当前账号. (之前的Key可能会被删除)" = "Prepare to bind the WeChat account. If the account you bind already exists, it will be merged into the current account. (The previous Key may be deleted)";
/* No comment provided by engineer. */
"自定义服务器" = "Custom server";
@ -157,3 +184,5 @@
/* No comment provided by engineer. */
"Endpoint 格式不正确" = "Endpoint is malformed";
/* No comment provided by engineer. */
"复制消息内容" = "Copy message content";

View File

@ -0,0 +1,447 @@
###### CONSISTENCY BETWEEN MACOS AND IOS #####
#
# In order to use the same PodFile with MacOS, we need to unlink the libraries that do not support Catalyst, filter
# files in native targets build phases, filter dependencies and make sure the unsupported frameworks along with their
# their bundle resources are not included in the final archive. For that, we use `platform_filter` to specify 'ios' and
# 'OTHER_LDFLAGS[sdk=iphone*]' to link those libraries for iPhone and iPad. Besides, we modify "*frameworks.sh" and
# "*resrouces.sh" to skip installation for architecture x86_64.
#
# *Notice*: 'sdk=iphone*' excludes macOS, even though Catalyst is compiled with iOS SDK.
#
# ADDING A NEW LIBRARY
#
# Pass the name of the new library to the script
#
###### RESOURCES #######
#
# https://www.bitbuildr.com/tech-blog/mac-catalyst-porting-an-app-using-crashlytics-firebase - Article that inspired this script
# https://github.com/CocoaPods/Xcodeproj - Xcode configuration using ruby. This Framework is already included on cocoapods environment
# https://www.rubydoc.info/gems/xcodeproj/Xcodeproj/Project/Object/AbstractTarget Wiki for Xcodeproj
#
include Xcodeproj::Project::Object
include Pod
$verbose = false
def loggs string
if $verbose
puts string
end
return
end
# EXTENSIONS
class String
def filter_lines
lines = []
each_line do |line|
if yield line
lines = lines + [line]
end
end
return lines
end
end
class PBXNativeTarget
###### STEP 4 ######
# In "Pods-" targets, modify "*frameworks.sh" to not install unsupported frameworks for platform architectures
def uninstall_frameworks frameworks, platform, configurations
uninstall frameworks, "#{name}-frameworks.sh", platform.architectures, configurations
end
###### STEP 5 ######
# In "Pods-" targets, modify "*resources.sh" to not install unsupported frameworks for platform architectures
def uninstall_resources resources, platform, configurations
uninstall resources, "#{name}-resources.sh", platform.architectures, configurations
end
def support_files_folder
build_configurations.filter do |config| not config.base_configuration_reference.nil? end.first.base_configuration_reference.real_path.parent
end
@private
def uninstall keys, file_name, architectures, configurations=nil
configurations = configurations.nil? ? build_configurations.map { |b| b.name } : configurations
keys = keys.to_set.to_a
loggs "\t\t\tUninstalling for configurations: #{configurations}"
if support_files_folder.nil?
loggs "\t\t\tNothing to uninstall"
return
end
script_path = support_files_folder.join file_name
if !script_path.exist?
loggs "\t\t\tNothing to uninstall"
return
end
script = File.read(script_path)
snippets = script.scan(/if \[\[ \"\$CONFIGURATION\" [\S\s]*?(?=fi\n)fi/)
condition = architectures.map do |arch| "[ \"$ARCHS\" != \"#{arch}\" ]" end.reduce("") do |total, piece| total.empty? ? piece : total + " || " + piece end
changed = false
snippets.filter do |snippet|
configurations.map do |string| snippet.include? string end.reduce(false) do |total, condition| total = total || condition end
end.each do |snippet|
new_snippet = snippet.clone
keys.each do |key|
lines_to_replace = snippet.filter_lines do |line| line.include? "#{key}" end.to_set.to_a
unless lines_to_replace.empty?
changed = true
lines_to_replace.each do |line|
new_snippet.gsub! line, "\tif #{condition}; then \n\t#{line}\tfi\n"
end
end
end
script.gsub! snippet, new_snippet
end
if changed
File.open(script_path, "w") { |file| file << script }
end
loggs "\t\t\t#{changed ? "Succeded" : "Nothing to uninstall"}"
end
###### STEP 1 ######
# In native target's build phases, add platform filter to:
# - Resources
# - Compile Sources
# - Frameworks
# - Headers
def add_platform_filter_to_build_phases platform
loggs "\t\t- Filtering resources"
resources_build_phase.files.to_a.map do |build_file| build_file.platform_filter = platform.name end
loggs "\t\t- Filtering compile sources"
source_build_phase.files.to_a.map do |build_file| build_file.platform_filter = platform.name end
loggs "\t\t- Filtering frameworks"
frameworks_build_phase.files.to_a.map do |build_file| build_file.platform_filter = platform.name end
loggs "\t\t- Filtering headers"
headers_build_phase.files.to_a.map do |build_file| build_file.platform_filter = platform.name end
end
end
class PodTarget
def module_name
string = name.clone.gsub! /-iOS[0-9]+(\.[0-9])+$/, ''
return string.nil? ? name : string
end
def resources
return file_accessors.flat_map do |accessor| accessor.resources end.map do |path| "#{path.basename}" end
end
def vendor_products
return file_accessors.flat_map do |accessor|
accessor.vendored_frameworks + accessor.vendored_libraries
end.map do |s| s.basename
end.map do |s|
name = "#{s}"
if name.include? "framework"
PodDependency.newFramework name.sub(".framework", "")
else
PodDependency.newLibrary name.sub("lib", "").sub(".a", "")
end
end
end
def frameworks
return file_accessors.flat_map do |accessor|
accessor.spec_consumer.frameworks.map do |name| PodDependency.newFramework name end + accessor.spec_consumer.libraries.map do |name| PodDependency.newLibrary name end
end
end
end
class PBXTargetDependency
def module_name
string = name.clone.gsub! /-iOS[0-9]+(\.[0-9])+$/, ''
return string.nil? ? name : string
end
end
class AbstractTarget
def module_name
string = name.clone.gsub! /-iOS[0-9]+(\.[0-9])+$/, ''
return string.nil? ? name : string
end
###### STEP 2 ######
# In all targets (aggregates + native), filter dependencies
def add_platform_filter_to_dependencies platform
loggs "\t\t- Filtering dependencies"
dependencies.each do |dependency|
dependency.platform_filter = platform.name
end
end
###### STEP 3 ######
# If any unsupported library, then flag as platform-dependant for every build configuration
def flag_libraries libraries, platform
loggs "\tTarget: #{name}"
build_configurations.filter do |config| not config.base_configuration_reference.nil?
end.each do |config|
loggs "\t\tScheme: #{config.name}"
xcconfig_path = config.base_configuration_reference.real_path
xcconfig = File.read(xcconfig_path)
changed = false
libraries.each do |framework|
if xcconfig.include? framework
xcconfig.gsub!(framework, '')
unless xcconfig.include? "OTHER_LDFLAGS[sdk=#{platform.sdk}]"
changed = true
xcconfig += "OTHER_LDFLAGS[sdk=#{platform.sdk}] = $(inherited) -ObjC "
end
xcconfig += framework + ' '
end
end
File.open(xcconfig_path, "w") { |file| file << xcconfig }
loggs "\t\t\t#{changed ? "Succeded" : "Nothing to flag"}"
end
end
def to_dependency
# We return both as we don't know if build as library or framework
return [PodDependency.newFramework(module_name), PodDependency.newLibrary(module_name)]
end
# Dependencies contained in Other Linker Flags
def other_linker_flags_dependencies
frameworks = Array.new
libraries = Array.new
config = build_configurations.filter do |config| not config.base_configuration_reference.nil? end.first
xcconfig_path = config.base_configuration_reference.real_path
xcconfig = File.read(xcconfig_path)
xcconfig.gsub!(/\r\n?/, "\n")
xcconfig.each_line do |line|
if line.start_with? 'OTHER_LDFLAGS'
frameworks = frameworks + line.split("-framework").map do |s|
s.strip.delete("\n") end.filter do |s|
s.strip.start_with? '"' end
libraries = libraries + line.split("-l").filter do |s| s.strip.start_with? '"' end.map do |s| s.strip.split(' ').first end
end
end
libraries = libraries.map do |name| PodDependency.newLibrary(name.gsub!("\"", "")) end
frameworks = frameworks.map do |name| PodDependency.newFramework(name.gsub!("\"", "")) end
return OtherLinkerFlagsDependencies.new libraries, frameworks
end
end
# HELPER CLASSES
class PodDependency
attr_reader :name
attr_reader :type
def link
if library?
return "-l\"#{name}\""
else
return "-framework \"#{name}\""
end
end
def library?
return type == "library"
end
def framework?
return type == "framework"
end
def self.newFramework name
return PodDependency.new(name, "framework")
end
def self.newLibrary name
return PodDependency.new(name, "library")
end
def ==(other)
name == other.name && type == other.type
end
def eql?(other)
name == other.name
end
private
def initialize(name, type)
@name = name
@type = type
end
end
class OtherLinkerFlagsDependencies
attr_reader :libraries
attr_reader :frameworks
def initialize(libraries = [], frameworks = [])
@libraries = libraries
@frameworks = frameworks
end
def combine dependencies
frameworks = (dependencies.frameworks + @frameworks).to_set.to_a
libraries = (dependencies.libraries + @libraries).to_set.to_a
return OtherLinkerFlagsDependencies.new libraries, frameworks
end
def dependencies
libraries + frameworks
end
end
class OSPlatform
attr_reader :sdk
attr_reader :name
attr_reader :architectures
def self.ios
OSPlatform.new 'ios', 'iphone*', ['arm64', 'armv7s', 'armv7']
end
def self.macos
OSPlatform.new 'macos', 'macosx*', ['x86_64']
end
def self.wtachos
OSPlatform.new 'watchos', 'watchos*', ['arm64_32', 'armv7k']
end
def self.tvos
OSPlatform.new 'tvos', 'appletvos*', ['arm64']
end
private
def initialize(name, sdk, architectures)
@name = name
@sdk = sdk
@architectures = architectures
end
end
# SCRIPT
class Installer
def configure_support_catalyst pod_names_to_keep, pod_names_to_remove, configurations=nil
###### Variable definition ######
targets = pods_project.targets
pod_names_to_remove = pod_names_to_remove.map do |name| name.sub('/', '') end
pod_names_to_keep = pod_names_to_keep.map do |name| name.sub('/', '') end
pod_names_to_keep = recursive_dependencies pod_names_to_keep
pod_names_to_remove = recursive_dependencies(pod_names_to_remove).filter do |name| !pod_names_to_keep.include? name end
pod_targets_to_keep = pod_targets.filter do |pod| pod_names_to_keep.include? pod.module_name end # PodTarget
pod_targets_to_remove = pod_targets.filter do |pod| pod_names_to_remove.include? pod.module_name end # PodTarget
loggs "\n#### Unsupported Libraries ####\n#{pod_names_to_remove}\n"
targets_to_remove = targets.filter do |target| pod_names_to_remove.include?(target.module_name) end # AbstractTarget
pods_targets = targets.filter do |target| target.name.start_with? "Pods-" end # AbstractTarget
cross_platform_targets = targets.filter do |target| !targets_to_remove.include?(target) && !pods_targets.include?(target) end # AbstractTarget
###### Determine which dependencies should be removed ######
dependencies_to_keep = cross_platform_targets.reduce(OtherLinkerFlagsDependencies.new) do |dependencies, target|
dependencies.combine target.other_linker_flags_dependencies
end.dependencies
# [PodDependency]
dependencies_to_keep = dependencies_to_keep + cross_platform_targets.flat_map do |target| target.to_dependency end + pod_targets_to_keep.flat_map do |pod| pod.vendor_products + pod.frameworks end
dependencies_to_remove = targets_to_remove.reduce(OtherLinkerFlagsDependencies.new) do |dependencies, target|
dependencies.combine target.other_linker_flags_dependencies
end.dependencies
# [PodDependency]
dependencies_to_remove = dependencies_to_remove + targets_to_remove.flat_map do |target| target.to_dependency end + pod_targets_to_remove.flat_map do |pod| pod.vendor_products + pod.frameworks end
dependencies_to_remove = dependencies_to_remove.filter do |d| !dependencies_to_keep.include? d end
###### CATALYST NOT SUPPORTED LINKS ######
unsupported_links = dependencies_to_remove.map do |d| d.link end.to_set.to_a
loggs "#### Unsupported dependencies ####\n"
loggs "#{dependencies_to_remove.map do |d| d.name end.to_set.to_a }\n\n"
###### CATALYST NOT SUPPORTED FRAMEWORKS AND RESOURCES
frameworks_to_uninstall = dependencies_to_remove.filter do |d| d.framework? end.map do |d| "#{d.name}.framework" end.to_set.to_a
resources_to_uninstall = pod_targets_to_remove.flat_map do |pod| pod.resources end.to_set.to_a
loggs "#### Frameworks not to be included in the Archive ####\n"
loggs "#{frameworks_to_uninstall}\n\n"
loggs "#### Resources not to be included in the Archive ####\n"
loggs "#{resources_to_uninstall}\n\n"
###### OTHER LINKER FLAGS -> to iphone* ######
loggs "#### Flagging unsupported libraries ####"
targets.each do |target| target.flag_libraries unsupported_links, OSPlatform.ios end
###### BUILD_PHASES AND DEPENDENCIES -> PLATFORM_FILTER 'ios' ######
loggs "\n#### Filtering build phases ####"
targets_to_remove.filter do |target|
pods_project.native_targets.include? target
end.each do |target|
loggs "\tTarget: #{target.name}"
target.add_platform_filter_to_build_phases OSPlatform.ios
target.add_platform_filter_to_dependencies OSPlatform.ios
end
loggs "\n#### Filtering dependencies ####"
targets_to_remove.filter do |target|
!pods_project.native_targets.include? target
end.each do |target|
loggs "\tTarget: #{target.name}"
target.add_platform_filter_to_dependencies OSPlatform.ios
end
###### FRAMEWORKS AND RESOURCES SCRIPT -> if [ "$ARCHS" != "x86_64" ]; then #######
loggs "\n#### Chagings frameworks and resources script ####"
pods_targets.each do |target|
loggs "\tTarget: #{target.name}"
loggs "\t\t-Uninstalling frameworks"
target.uninstall_frameworks frameworks_to_uninstall, OSPlatform.macos, configurations
loggs "\t\t-Uninstalling resources"
target.uninstall_resources resources_to_uninstall, OSPlatform.macos, configurations
end
end
@private
def recursive_dependencies to_filter_names
targets = pods_project.targets
targets_to_remove = targets.filter do |target| to_filter_names.include? target.module_name end
dependencies = targets_to_remove.flat_map do |target| target.dependencies end
dependencies_names = dependencies.map do |d| d.module_name end
if dependencies.empty?
return to_filter_names + dependencies_names
else
return to_filter_names + recursive_dependencies(dependencies_names)
end
end
end