Merge pull request #13 from Hext123/main

新增: 消息本地缓存 / 国际化(英文/中文) / 其它细节优化
This commit is contained in:
Easy 2022-01-15 22:18:12 +08:00 committed by GitHub
commit 3d76f0dbec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 673 additions and 170 deletions

View File

@ -10,7 +10,7 @@ def commonPods
pod 'SDWebImageSwiftUI', '~> 2.0.2'
pod 'KRProgressHUD', '~> 3.4.7'
# pod 'WoodPeckeriOS', :configurations => ['Debug']
# pod 'WoodPeckeriOS', :configurations => ['Debug']
end
target 'PushDeer' do

View File

@ -35,6 +35,6 @@ SPEC CHECKSUMS:
SDWebImage: 4dc3e42d9ec0c1028b960a33ac6b637bb432207b
SDWebImageSwiftUI: 8a3923c95108312b03a599ec1498754af55a6819
PODFILE CHECKSUM: e462e86a9cce18b92c573f662ef405e7091cd912
PODFILE CHECKSUM: 06aae1de50f9c1a188e69787835ec8718dd7d543
COCOAPODS: 1.11.2

View File

@ -15,6 +15,8 @@
52163EBB277741AC00594190 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52163EBA277741AC00594190 /* SettingsView.swift */; };
523150D9277875FB00941EDC /* DeletableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 523150D8277875FB00941EDC /* DeletableView.swift */; };
523150DC2778762B00941EDC /* DeviceItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 523150DB2778762B00941EDC /* DeviceItemView.swift */; };
5242C872278C8CBB00FDB27E /* EditableText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5242C871278C8CBB00FDB27E /* EditableText.swift */; };
5242C873278C8CBB00FDB27E /* EditableText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5242C871278C8CBB00FDB27E /* EditableText.swift */; };
52450F3827848243003652D8 /* PushDeerApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450F3727848243003652D8 /* PushDeerApi.swift */; };
52450F3927848243003652D8 /* PushDeerApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450F3727848243003652D8 /* PushDeerApi.swift */; };
52450F3B278491F8003652D8 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450F3A278491F8003652D8 /* AppState.swift */; };
@ -25,6 +27,13 @@
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 */; };
526A1E702791E00400BA2177 /* PushDeerData.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 526A1E6E2791E00400BA2177 /* PushDeerData.xcdatamodeld */; };
526A1E712791E00400BA2177 /* PushDeerData.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 526A1E6E2791E00400BA2177 /* PushDeerData.xcdatamodeld */; };
526A1E732791E03900BA2177 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526A1E722791E03900BA2177 /* Persistence.swift */; };
526A1E742791E03900BA2177 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526A1E722791E03900BA2177 /* Persistence.swift */; };
526A1E752791E52600BA2177 /* PushDeerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5292F4F82776BC7900B9A7BB /* PushDeerApp.swift */; };
526A1E7D2792B2A600BA2177 /* MessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526A1E7C2792B2A600BA2177 /* MessageModel.swift */; };
526A1E7E2792B2A600BA2177 /* MessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526A1E7C2792B2A600BA2177 /* MessageModel.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 */; };
@ -34,7 +43,6 @@
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 */; };
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, ); }; };
@ -54,6 +62,10 @@
52B8CF85277E0C12004CB680 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB90B22778DA4E0048E0ED /* Line.swift */; };
52B8CF86277E0C12004CB680 /* BaseNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB90AD2778AFD60048E0ED /* BaseNavigationView.swift */; };
52B8CF87277E0C5C004CB680 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5292F4FC2776BC7A00B9A7BB /* Assets.xcassets */; };
52E317D9279305BB000B8BB1 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 52E317D7279305BB000B8BB1 /* InfoPlist.strings */; };
52E317DC279305BB000B8BB1 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 52E317DA279305BB000B8BB1 /* Localizable.strings */; };
52E317DF279305BB000B8BB1 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 52E317DD279305BB000B8BB1 /* InfoPlist.strings */; };
52E317E727930AA4000B8BB1 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 52E317DA279305BB000B8BB1 /* Localizable.strings */; };
52EB90AC2778ADF80048E0ED /* CardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB90AB2778ADF80048E0ED /* CardView.swift */; };
52EB90AE2778AFD60048E0ED /* BaseNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB90AD2778AFD60048E0ED /* BaseNavigationView.swift */; };
52EB90B02778D67F0048E0ED /* KeyItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB90AF2778D67F0048E0ED /* KeyItemView.swift */; };
@ -97,12 +109,16 @@
52163EBA277741AC00594190 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
523150D8277875FB00941EDC /* DeletableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletableView.swift; sourceTree = "<group>"; };
523150DB2778762B00941EDC /* DeviceItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceItemView.swift; sourceTree = "<group>"; };
5242C871278C8CBB00FDB27E /* EditableText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditableText.swift; sourceTree = "<group>"; };
52450F3727848243003652D8 /* PushDeerApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushDeerApi.swift; sourceTree = "<group>"; };
52450F3A278491F8003652D8 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
52450F3E2784923D003652D8 /* Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
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>"; };
526A1E6F2791E00400BA2177 /* PushDeerData.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = PushDeerData.xcdatamodel; sourceTree = "<group>"; };
526A1E722791E03900BA2177 /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
526A1E7C2792B2A600BA2177 /* MessageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageModel.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>"; };
@ -111,11 +127,16 @@
5292F4FF2776BC7A00B9A7BB /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
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>"; };
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>"; };
52B8CF70277E0B46004CB680 /* PushDeerClip.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PushDeerClip.entitlements; 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>"; };
52E317E827930AD0000B8BB1 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
52E317E927930C16000B8BB1 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
52E317EA27930C21000B8BB1 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
52EB90AB2778ADF80048E0ED /* CardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardView.swift; sourceTree = "<group>"; };
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>"; };
@ -171,6 +192,7 @@
52450F3727848243003652D8 /* PushDeerApi.swift */,
52450F3A278491F8003652D8 /* AppState.swift */,
52450F412784943F003652D8 /* HttpRequest.swift */,
526A1E722791E03900BA2177 /* Persistence.swift */,
);
path = Service;
sourceTree = "<group>";
@ -179,6 +201,8 @@
isa = PBXGroup;
children = (
52450F3E2784923D003652D8 /* Result.swift */,
526A1E7C2792B2A600BA2177 /* MessageModel.swift */,
526A1E6E2791E00400BA2177 /* PushDeerData.xcdatamodeld */,
);
path = Model;
sourceTree = "<group>";
@ -208,6 +232,8 @@
isa = PBXGroup;
children = (
52450F442784A95D003652D8 /* Info.plist */,
52E317DA279305BB000B8BB1 /* Localizable.strings */,
52E317D7279305BB000B8BB1 /* InfoPlist.strings */,
52450F362784822C003652D8 /* Service */,
52B8CF5D277DE5FF004CB680 /* Common */,
52450F3D27849228003652D8 /* Model */,
@ -242,9 +268,9 @@
52B8CF65277E0B44004CB680 /* PushDeerClip */ = {
isa = PBXGroup;
children = (
52B8CF66277E0B44004CB680 /* PushDeerClipApp.swift */,
52B8CF6A277E0B46004CB680 /* Assets.xcassets */,
52B8CF6F277E0B46004CB680 /* Info.plist */,
52E317DD279305BB000B8BB1 /* InfoPlist.strings */,
52B8CF70277E0B46004CB680 /* PushDeerClip.entitlements */,
52B8CF6C277E0B46004CB680 /* Preview Content */,
);
@ -266,6 +292,7 @@
523150D8277875FB00941EDC /* DeletableView.swift */,
52EB90AB2778ADF80048E0ED /* CardView.swift */,
52EB90B22778DA4E0048E0ED /* Line.swift */,
5242C871278C8CBB00FDB27E /* EditableText.swift */,
);
path = Common;
sourceTree = "<group>";
@ -371,6 +398,7 @@
knownRegions = (
en,
Base,
"zh-Hans",
);
mainGroup = 5292F4EC2776BC7900B9A7BB;
packageReferences = (
@ -392,6 +420,8 @@
buildActionMask = 2147483647;
files = (
5292F5002776BC7A00B9A7BB /* Preview Assets.xcassets in Resources */,
52E317DC279305BB000B8BB1 /* Localizable.strings in Resources */,
52E317D9279305BB000B8BB1 /* InfoPlist.strings in Resources */,
5292F4FD2776BC7A00B9A7BB /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -400,7 +430,9 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
52E317E727930AA4000B8BB1 /* Localizable.strings in Resources */,
52B8CF6E277E0B46004CB680 /* Preview Assets.xcassets in Resources */,
52E317DF279305BB000B8BB1 /* InfoPlist.strings in Resources */,
52B8CF87277E0C5C004CB680 /* Assets.xcassets in Resources */,
52B8CF6B277E0B46004CB680 /* Assets.xcassets in Resources */,
);
@ -498,8 +530,10 @@
523150DC2778762B00941EDC /* DeviceItemView.swift in Sources */,
523150D9277875FB00941EDC /* DeletableView.swift in Sources */,
52163EBB277741AC00594190 /* SettingsView.swift in Sources */,
5242C872278C8CBB00FDB27E /* EditableText.swift in Sources */,
52163EB327773F8400594190 /* MainView.swift in Sources */,
52EB90B32778DA4E0048E0ED /* Line.swift in Sources */,
526A1E732791E03900BA2177 /* Persistence.swift in Sources */,
52F2C223277961D7006F08DC /* SettingsItemView.swift in Sources */,
52163EB72777415F00594190 /* DeviceListView.swift in Sources */,
52B8CF5F277DE660004CB680 /* AppleSignInButton.swift in Sources */,
@ -508,12 +542,14 @@
5287FF59278B3AAE00249A0E /* HToast.swift in Sources */,
52450F3B278491F8003652D8 /* AppState.swift in Sources */,
5292F4F92776BC7900B9A7BB /* PushDeerApp.swift in Sources */,
526A1E7D2792B2A600BA2177 /* MessageModel.swift in Sources */,
52483FC2277ED6D5003A100E /* AppDelegate.swift in Sources */,
52450F3827848243003652D8 /* PushDeerApi.swift in Sources */,
52EB90AE2778AFD60048E0ED /* BaseNavigationView.swift in Sources */,
52EB90AC2778ADF80048E0ED /* CardView.swift in Sources */,
52EB90B02778D67F0048E0ED /* KeyItemView.swift in Sources */,
52450F3F2784923D003652D8 /* Result.swift in Sources */,
526A1E702791E00400BA2177 /* PushDeerData.xcdatamodeld in Sources */,
52450F422784943F003652D8 /* HttpRequest.swift in Sources */,
52163EB52777413B00594190 /* MessageListView.swift in Sources */,
52F40D2F277CA05600766C24 /* MessageItemView.swift in Sources */,
@ -528,8 +564,10 @@
52B8CF78277E0BF1004CB680 /* MainView.swift in Sources */,
52B8CF79277E0BFB004CB680 /* DeviceListView.swift in Sources */,
52B8CF84277E0C12004CB680 /* CardView.swift in Sources */,
5242C873278C8CBB00FDB27E /* EditableText.swift in Sources */,
52B8CF7E277E0BFB004CB680 /* SettingsView.swift in Sources */,
52B8CF7C277E0BFB004CB680 /* KeyListView.swift in Sources */,
526A1E742791E03900BA2177 /* Persistence.swift in Sources */,
52B8CF7A277E0BFB004CB680 /* MessageListView.swift in Sources */,
52B8CF83277E0C12004CB680 /* DeletableView.swift in Sources */,
52B8CF81277E0BFB004CB680 /* LoginView.swift in Sources */,
@ -537,13 +575,15 @@
52450F3C278491F8003652D8 /* AppState.swift in Sources */,
5287FF5A278B3AAE00249A0E /* HToast.swift in Sources */,
52B8CF80277E0BFB004CB680 /* MessageItemView.swift in Sources */,
526A1E752791E52600BA2177 /* PushDeerApp.swift in Sources */,
526A1E7E2792B2A600BA2177 /* MessageModel.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 */,
526A1E712791E00400BA2177 /* PushDeerData.xcdatamodeld in Sources */,
52450F432784943F003652D8 /* HttpRequest.swift in Sources */,
52B8CF7B277E0BFB004CB680 /* DeviceItemView.swift in Sources */,
52B8CF7D277E0BFB004CB680 /* KeyItemView.swift in Sources */,
@ -561,11 +601,42 @@
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
52E317D7279305BB000B8BB1 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
52E317D8279305BB000B8BB1 /* en */,
52E317EA27930C21000B8BB1 /* zh-Hans */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
52E317DA279305BB000B8BB1 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
52E317DB279305BB000B8BB1 /* en */,
52E317E827930AD0000B8BB1 /* zh-Hans */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
52E317DD279305BB000B8BB1 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
52E317DE279305BB000B8BB1 /* en */,
52E317E927930C16000B8BB1 /* zh-Hans */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
5292F5012776BC7A00B9A7BB /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
@ -627,6 +698,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
@ -689,6 +761,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"PushDeer/Preview Content\"";
DEVELOPMENT_TEAM = Y47WTLML2S;
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PushDeer/Info.plist;
@ -722,6 +795,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"PushDeer/Preview Content\"";
DEVELOPMENT_TEAM = Y47WTLML2S;
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PushDeer/Info.plist;
@ -877,6 +951,19 @@
productName = MarkdownUI;
};
/* End XCSwiftPackageProductDependency section */
/* Begin XCVersionGroup section */
526A1E6E2791E00400BA2177 /* PushDeerData.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
526A1E6F2791E00400BA2177 /* PushDeerData.xcdatamodel */,
);
currentVersion = 526A1E6F2791E00400BA2177 /* PushDeerData.xcdatamodel */;
path = PushDeerData.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
};
/* End XCVersionGroup section */
};
rootObject = 5292F4ED2776BC7900B9A7BB /* Project object */;
}

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1320"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "52B8CF63277E0B44004CB680"
BuildableName = "PushDeerClip.app"
BlueprintName = "PushDeerClip"
ReferencedContainer = "container:PushDeer.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "52B8CF63277E0B44004CB680"
BuildableName = "PushDeerClip.app"
BlueprintName = "PushDeerClip"
ReferencedContainer = "container:PushDeer.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "_XCAppClipURL"
value = "https://example.com"
isEnabled = "NO">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "52B8CF63277E0B44004CB680"
BuildableName = "PushDeerClip.app"
BlueprintName = "PushDeerClip"
ReferencedContainer = "container:PushDeer.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -35,6 +35,11 @@ class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDele
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
print("willPresent:", notification.request.content.userInfo)
Task {
// ,
let messageItems = try await HttpRequest.getMessages().messages
try MessageModel.saveAndUpdate(messageItems: messageItems)
}
return [.sound, .list, .banner]
}

View File

@ -16,6 +16,12 @@ struct HToast {
static func showSuccess(_ msg: String?) {
KRProgressHUD.showSuccess(withMessage: msg)
}
static func showInfo(_ msg: String?) {
KRProgressHUD.showInfo(withMessage: msg)
}
static func showWarning(_ msg: String?) {
KRProgressHUD.showWarning(withMessage: msg)
}
static func showError(_ msg: String?) {
KRProgressHUD.showError(withMessage: msg)
}

View File

@ -0,0 +1,87 @@
//
// MessageModel.swift
// PushDeer
//
// Created by HEXT on 2022/1/15.
//
import Foundation
import CoreData
extension MessageModel {
convenience init(id: Int64, uid: String, text: String, desp: String, type: String, pushkey_name: String, created_at: String, context: NSManagedObjectContext = PersistenceController.shared.container.viewContext) {
self.init(context: context)
self.id = id
self.uid = uid
self.text = text
self.desp = desp
self.type = type
self.pushkey_name = pushkey_name
self.created_at = created_at
}
convenience init(messageItem: MessageItem, context: NSManagedObjectContext = PersistenceController.shared.container.viewContext) {
self.init(
id: Int64(messageItem.id),
uid: messageItem.uid,
text: messageItem.text,
desp: messageItem.desp,
type: messageItem.type,
pushkey_name: messageItem.pushkey_name,
created_at: messageItem.created_at,
context: context)
}
var createdDateStr: String {
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"
let createdDate = dateFormatter.date(from: self.created_at ?? "") ?? Date()
let timeInterval = -createdDate.timeIntervalSinceNow
let minute = Int(floor(timeInterval / 60))
if minute == 0 {
return "刚刚"
} else if minute <= 30 {
return "\(minute)分钟前"
} else if Calendar.current.isDateInToday(createdDate) {
dateFormatter.dateFormat = "HH:mm:ss"
} else {
dateFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss"
}
return dateFormatter.string(from: createdDate)
}
static let _viewContext = PersistenceController.shared.container.viewContext
static let _fetchRequest = MessageModel.fetchRequest()
///
static func saveAndUpdate(messageItems: [MessageItem]) throws -> Void {
try messageItems.forEach(saveAndUpdate)
}
///
static func saveAndUpdate(messageItem: MessageItem) throws -> Void {
_fetchRequest.predicate = NSPredicate(format: "id = \(messageItem.id)")
let models = try _viewContext.fetch(_fetchRequest)
if models.isEmpty {
// , context
_ = MessageModel(messageItem: messageItem, context: _viewContext)
} else {
// , ,
models.enumerated().forEach { element in
let messageModel = element.element
let index = element.offset
if index == 0 {
messageModel.id = Int64(messageItem.id);
messageModel.uid = messageItem.uid;
messageModel.text = messageItem.text;
messageModel.desp = messageItem.desp;
messageModel.type = messageItem.type;
messageModel.pushkey_name = messageItem.pushkey_name;
messageModel.created_at = messageItem.created_at;
} else {
_viewContext.delete(messageModel)
}
}
}
// context
try _viewContext.save()
}
}

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21C52" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="MessageModel" representedClassName="MessageModel" syncable="YES" codeGenerationType="class">
<attribute name="created_at" optional="YES" attributeType="String"/>
<attribute name="desp" optional="YES" attributeType="String"/>
<attribute name="id" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="pushkey_name" optional="YES" attributeType="String"/>
<attribute name="text" optional="YES" attributeType="String"/>
<attribute name="type" optional="YES" attributeType="String"/>
<attribute name="uid" optional="YES" attributeType="String"/>
</entity>
<elements>
<element name="MessageModel" positionX="-45" positionY="1" width="128" height="148"/>
</elements>
</model>

View File

@ -34,7 +34,7 @@ struct UserInfoContent: Codable{
struct DeviceItem: Codable, Identifiable{
let id: Int
let uid: String
let name: String
var name: String
let type: String
let device_id: String
let is_clip: Int
@ -50,7 +50,7 @@ struct KeyContent: Codable{
struct KeyItem: Codable, Identifiable{
let id: Int
let name: String
var name: String
let uid: String
let key: String
let created_at: String
@ -66,6 +66,7 @@ struct MessageItem: Codable, Identifiable{
let text: String
let desp: String
let type: String
let pushkey_name: String
let created_at: String
}
@ -87,16 +88,3 @@ extension KeyItem {
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

@ -12,5 +12,7 @@
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.personal-information.photos-library</key>
<true/>
</dict>
</plist>

View File

@ -10,10 +10,14 @@ import SwiftUI
@main
struct PushDeerApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
let store = AppState.shared
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView().environmentObject(AppState.shared)
ContentView()
.environmentObject(store)
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}

View File

@ -20,7 +20,7 @@ class AppState: ObservableObject {
/// key
@Published var keys: [KeyItem] = []
///
@Published var messages: [MessageItem] = []
// @Published var messages: [MessageItem] = []
/// tab
@Published var tabSelectedIndex: Int {
didSet {
@ -85,7 +85,7 @@ class AppState: ObservableObject {
print(error)
}
//
throw NSError(domain: "登录失败", code: -1, userInfo: nil)
throw NSError(domain: NSLocalizedString("登录失败", comment: "AppleId登录失败时提示"), code: -1, userInfo: nil)
}
}

View File

@ -26,9 +26,9 @@ struct HttpRequest {
continuation.resume(returning: content)
} else if result.code == 80403 {
AppState.shared.token = ""
continuation.resume(throwing: NSError(domain: result.error ?? "接口报错", code: result.code, userInfo: nil))
continuation.resume(throwing: NSError(domain: result.error ?? NSLocalizedString("登录过期", comment: "token失效时提示"), code: result.code, userInfo: nil))
} else {
continuation.resume(throwing: NSError(domain: result.error ?? "接口报错", code: result.code, userInfo: nil))
continuation.resume(throwing: NSError(domain: result.error ?? NSLocalizedString("接口报错", comment: "接口报错时提示"), code: result.code, userInfo: nil))
}
} catch {
print(error)
@ -66,7 +66,9 @@ struct HttpRequest {
static func rmDevice(id: Int) async throws -> ActionContent {
return try await request(.rmDevice(token: AppState.shared.token, id: id), resultType: ActionContent.self)
}
static func renameDevice(id: Int, name: String) async throws -> ActionContent {
return try await request(.renameDevice(token: AppState.shared.token, id: id, name: name), resultType: ActionContent.self)
}
static func getDevices() async throws -> DeviceContent {
return try await request(.getDevices(token: AppState.shared.token), resultType: DeviceContent.self)
}

View File

@ -0,0 +1,39 @@
//
// Persistence.swift
// PushDeer
//
// Created by HEXT on 2022/1/14.
//
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "PushDeerData")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}
}

View File

@ -15,6 +15,7 @@ enum PushDeerApi {
case getUserInfo(token: String)
case regDevice(token: String, name: String, device_id: String, is_clip: Int)
case renameDevice(token: String, id: Int, name: String)
case getDevices(token: String)
case rmDevice(token: String, id: Int)
@ -45,6 +46,8 @@ extension PushDeerApi: TargetType {
case .regDevice:
return "/device/reg"
case .renameDevice:
return "/device/rename"
case .getDevices:
return "/device/list"
case .rmDevice:
@ -87,6 +90,8 @@ extension PushDeerApi: TargetType {
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)
case let .renameDevice(token, id, name):
return .requestParameters(parameters: ["token": token,"id": id,"name": name], encoding: URLEncoding.queryString)
case let .getDevices(token):
return .requestParameters(parameters: ["token": token], encoding: URLEncoding.queryString)
case let .rmDevice(token, id):

View File

@ -10,15 +10,20 @@ import SwiftUI
/// View, APP使
struct BaseNavigationView<Content : View> : View {
///
let title: String
let title: LocalizedStringKey
/// View
@ViewBuilder let contentView: Content
var body: some View {
NavigationView {
ZStack {
Spacer()
.frame(width: .infinity, height: .infinity)
// VStack HStack Spacer
VStack {
HStack {
Spacer()
}
Spacer()
}
contentView
}
.background(

View File

@ -71,7 +71,7 @@ struct DeletableView<Content : View> : View {
struct DeletableView_Previews: PreviewProvider {
static var previews: some View {
DeletableView(contentView: {
DeviceItemView(name: "未知设备")
DeviceItemView(deviceItem: DeviceItem(id: 0, uid: "", name: "Hext's iPhone 11", type: "", device_id: "", is_clip: 0))
}, deleteAction: {
})

View File

@ -0,0 +1,47 @@
//
// EditableText.swift
// PushDeer
//
// Created by HEXT on 2022/1/10.
//
import SwiftUI
struct EditableText: View {
var placeholder = ""
@State var value = ""
var onCommit: (_ value: String) -> Void = { value in
}
func textField() -> some View {
TextField(placeholder, text: $value, onCommit: {
print("修改文本:", value)
self.onCommit(value)
})
.font(.system(size: 20))
.foregroundColor(Color.accentColor)
}
var body: some View {
if #available(iOS 15.0, *) {
textField()
.submitLabel(.done)
} else {
textField()
}
}
}
struct EditableText_Previews: PreviewProvider {
static var previews: some View {
VStack {
EditableText(value: "你好")
EditableText(placeholder: "请输入")
EditableText()
EditableText(placeholder: "请输入") { value in
}
}
}
}

View File

@ -9,16 +9,40 @@ import SwiftUI
/// View
struct DeviceItemView: View {
var name: String
let deviceItem: DeviceItem
@EnvironmentObject private var store: AppState
var body: some View {
CardView {
HStack{
Image(systemName: getSystemName(deviceName: name))
.resizable()
.scaledToFit()
.frame(width: 40, height: 40, alignment: .center)
.padding(EdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 8))
Text(name)
ZStack{
Image(systemName: getSystemName(deviceName: deviceItem.name))
.resizable()
.scaledToFit()
.frame(width: 40, height: 40, alignment: .center)
if deviceItem.is_clip == 1 {
Image(systemName: "appclip")
.resizable()
.scaledToFit()
.frame(width: 12, height: 12, alignment: .center)
}
}
.padding(EdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 8))
EditableText(placeholder: NSLocalizedString("输入设备名称", comment: ""), value: deviceItem.name) { value in
Task {
//
_ = try await HttpRequest.renameDevice(id: deviceItem.id, name: value)
HToast.showSuccess(NSLocalizedString("已修改设备名称", comment: ""))
// Item
let index = store.devices.firstIndex { $0.id == deviceItem.id }
if let index = index {
// Item
store.devices[index].name = value
}
}
}
Text(getInfo(deviceItem: deviceItem))
.font(.system(size: 20))
Spacer()
}
@ -26,6 +50,17 @@ struct DeviceItemView: View {
}
}
func getInfo(deviceItem: DeviceItem) -> String {
var name = ""
// if deviceItem.is_clip == 1 {
// name += " [Clip]"
// }
if deviceItem.device_id == store.deviceToken {
name += NSLocalizedString("(当前设备)", comment: "在设备列表中标识当前设备")
}
return name
}
func getSystemName(deviceName: String) -> String {
var deviceName = deviceName.lowercased()
deviceName = deviceName.replacingOccurrences(of: " ", with: "")
@ -60,6 +95,7 @@ struct DeviceItemView: View {
struct DeviceItemView_Previews: PreviewProvider {
static var previews: some View {
DeviceItemView(name: "未知设备")
DeviceItemView(deviceItem: DeviceItem(id: 0, uid: "", name: "Hext's iPhone 11", type: "", device_id: "", is_clip: 1))
.environmentObject(AppState.shared)
}
}

View File

@ -16,12 +16,12 @@ struct DeviceListView: View {
LazyVStack(alignment: .center) {
ForEach(store.devices.reversed()) { deviceItem in
DeletableView(contentView: {
DeviceItemView(name: getName(deviceItem: deviceItem))
DeviceItemView(deviceItem: deviceItem)
}, deleteAction: {
store.devices.removeAll { _deviceItem in
_deviceItem.id == deviceItem.id
}
HToast.showSuccess("已删除")
HToast.showSuccess(NSLocalizedString("已删除", comment: "删除设备/Key/消息时提示"))
Task {
do {
_ = try await HttpRequest.rmDevice(id: deviceItem.id)
@ -37,11 +37,16 @@ struct DeviceListView: View {
}
.navigationBarItems(trailing: Button(action: {
Task {
let hasContains = store.devices.contains { store.deviceToken == $0.device_id }
if hasContains {
HToast.showInfo(NSLocalizedString("已添加过当前设备", comment: ""))
return;
}
let devices = try await HttpRequest.regDevice().devices
withAnimation(.easeOut) {
store.devices = devices
}
HToast.showSuccess("已添加当前设备")
HToast.showSuccess(NSLocalizedString("已添加当前设备", comment: ""))
}
}, label: {
Image(systemName: "plus")
@ -52,17 +57,6 @@ struct DeviceListView: View {
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 {

View File

@ -7,60 +7,10 @@
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
@EnvironmentObject private var store: AppState
var body: some View {
VStack(spacing: 20) {
@ -69,7 +19,19 @@ struct KeyItemView: View {
.resizable()
.scaledToFit()
.frame(width: 38, height: 38)
KeyItemTextField(keyItem: keyItem)
EditableText(placeholder: NSLocalizedString("输入key名称", comment: ""), value: keyItem.name) { value in
Task {
//
_ = try await HttpRequest.renameKey(id: keyItem.id, name: value)
HToast.showSuccess(NSLocalizedString("已修改key名称", comment: ""))
// keyItem
let index = store.keys.firstIndex { $0.id == keyItem.id }
if let index = index {
// keyItem
store.keys[index].name = value
}
}
}
Spacer()
Image(systemName: "calendar")
.font(.system(size: 14))
@ -89,13 +51,13 @@ struct KeyItemView: View {
HLine().stroke(Color.gray, style: StrokeStyle(lineWidth: 1, dash: [5]))
HStack {
Button("重置") {
Button(NSLocalizedString("重置", comment: "重置key的按钮标题")) {
print("点击重置")
Task {
do {
_ = try await HttpRequest.regenKey(id: keyItem.id)
HttpRequest.loadKeys()
HToast.showSuccess("已重置")
HToast.showSuccess(NSLocalizedString("已重置", comment: "已重置key的提示"))
} catch {
}
@ -108,10 +70,10 @@ struct KeyItemView: View {
Spacer()
Button("复制") {
Button(NSLocalizedString("复制", comment: "复制按钮的标题")) {
print("点击复制")
UIPasteboard.general.string = keyItem.key
HToast.showSuccess("已复制")
HToast.showSuccess(NSLocalizedString("已复制", comment: ""))
}
.font(.system(size: 20))
.frame(width: 90, height: 42)
@ -127,5 +89,6 @@ struct KeyItemView: View {
struct KeyItemView_Previews: PreviewProvider {
static var previews: some View {
KeyItemView(keyItem: KeyItem(id: 1, name: "name", uid: "1", key: "Key", created_at: "1111"))
.environmentObject(AppState.shared)
}
}

View File

@ -24,7 +24,7 @@ struct KeyListView: View {
store.keys.removeAll { _keyItem in
keyItem.id == _keyItem.id
}
HToast.showSuccess("已删除")
HToast.showSuccess(NSLocalizedString("已删除", comment: "删除设备/Key/消息时提示"))
Task {
do {
_ = try await HttpRequest.rmKey(id: keyItem.id)
@ -44,7 +44,7 @@ struct KeyListView: View {
withAnimation(.easeOut) {
store.keys = keys
}
HToast.showSuccess("已添加新Key")
HToast.showSuccess(NSLocalizedString("已添加新Key", comment: ""))
}
}, label: {
Image(systemName: "plus")

View File

@ -11,7 +11,7 @@ import SDWebImageSwiftUI
import Photos
struct MessageItemView: View {
let messageItem: MessageItem
let messageItem: MessageModel
///
let deleteAction : () -> ()
@ -24,7 +24,7 @@ struct MessageItemView: View {
.resizable()
.scaledToFit()
.frame(width: 38, height: 38)
Text("key名字")
Text(messageItem.pushkey_name ?? "")
.font(.system(size: 14))
.foregroundColor(Color(UIColor.darkGray))
Text(messageItem.createdDateStr)
@ -45,7 +45,7 @@ struct MessageItemView: View {
}
struct MessageContentView: View {
let messageItem: MessageItem
let messageItem: MessageModel
@State private var image: PlatformImage? = nil
var body: some View {
@ -53,15 +53,15 @@ struct MessageContentView: View {
case "markdown":
CardView {
VStack(alignment: .leading, spacing: 5) {
Markdown(Document(messageItem.text))
Markdown(Document(messageItem.text ?? ""))
.markdownStyle(
DefaultMarkdownStyle(
font: .system(size: 14),
foregroundColor: UIColor.darkGray
)
)
if !messageItem.desp.isEmpty {
Markdown(Document(messageItem.desp))
if !(messageItem.desp?.isEmpty ?? true) {
Markdown(Document(messageItem.desp!))
.markdownStyle(
DefaultMarkdownStyle(
font: .system(size: 14),
@ -74,31 +74,52 @@ struct MessageContentView: View {
}
case "image":
WebImage(url: URL(string: messageItem.text))
WebImage(url: URL(string: messageItem.text ?? ""))
.onSuccess { image, data, cacheType in
self.image = image
DispatchQueue.main.async {
self.image = image
}
}
.resizable()
.placeholder(content: {
ZStack {
Color.gray.opacity(0.5)
Image(systemName: "photo")
.foregroundColor(.gray)
.font(.system(size: 100))
}
.frame(width: nil, height: 200, alignment: .center)
})
.indicator(.activity)
.transition(.fade(duration: 0.5))
.scaledToFill()
.background(Color.white)
.contextMenu {
Button {
guard let image = image else {
HToast.showWarning(NSLocalizedString("图片未加载成功", comment: ""))
return
}
UIPasteboard.general.image = image
HToast.showSuccess("已拷贝")
HToast.showSuccess(NSLocalizedString("已拷贝", comment: ""))
} label: {
Label("拷贝图片",systemImage: "doc.on.doc")
}
Button {
guard let image = image else { return }
guard let image = image else {
HToast.showWarning(NSLocalizedString("图片未加载成功", comment: ""))
return
}
PHPhotoLibrary.shared().performChanges {
PHAssetChangeRequest.creationRequestForAsset(from: image)
} completionHandler: { (isSuccess, error) in
DispatchQueue.main.async {
if isSuccess {//
print("Success")
HToast.showSuccess("保存成功")
HToast.showSuccess(NSLocalizedString("保存成功", comment: ""))
} else {
print(error as Any)
HToast.showError("保存失败")
HToast.showError(NSLocalizedString("保存失败", comment: ""))
}
}
}
@ -111,13 +132,13 @@ struct MessageContentView: View {
CardView {
VStack(alignment: .leading, spacing: 5) {
HStack{
Text(messageItem.text)
Text(messageItem.text ?? "")
.font(.system(size: 14))
.foregroundColor(Color(UIColor.darkGray))
Spacer(minLength: 0)
}
if !messageItem.desp.isEmpty {
Text(messageItem.desp)
if !(messageItem.desp?.isEmpty ?? true) {
Text(messageItem.desp ?? "")
.font(.system(size: 14))
.foregroundColor(Color(UIColor.darkGray))
}
@ -125,8 +146,8 @@ struct MessageContentView: View {
.padding()
.contextMenu {
Button {
UIPasteboard.general.string = messageItem.text + messageItem.desp
HToast.showSuccess("已复制")
UIPasteboard.general.string = (messageItem.text ?? "") + (messageItem.desp ?? "")
HToast.showSuccess(NSLocalizedString("已复制", comment: ""))
} label: {
Label("复制",systemImage: "doc.on.doc")
}
@ -139,18 +160,18 @@ struct MessageContentView: View {
struct MessageItemView_Previews: PreviewProvider {
static var previews: some View {
VStack {
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: """
MessageItemView(messageItem: MessageModel(id: 1, uid: "1", text: "纯文本的效果", desp: "你好呀", type: "text", pushkey_name: "Key", created_at: "2022-01-08T18:00:48.000000Z")){}
MessageItemView(messageItem: MessageModel(id: 1, uid: "1", text: "纯文本的效果纯文本的效果纯文本的效果纯文本的效果纯文本的效果纯文本的效果纯文本的效果", desp: "", type: "text", pushkey_name: "Key", created_at: "2022-01-08T18:00:48.000000Z")){}
MessageItemView(messageItem: MessageModel(id: 1, uid: "1", text: "https://blog.wskfz.com/usr/uploads/2018/06/2498727457.png", desp: "", type: "image", pushkey_name: "Key1", created_at: "2022-01-08T18:00:48.000000Z")){}
MessageItemView(messageItem: MessageModel(id: 1, uid: "1", text: "https://blog.wskfz.com/usr/uploads/2018/06/2151130181.png", desp: "", type: "image", pushkey_name: "Key2", created_at: "2022-01-08T18:00:48.000000Z")){}
MessageItemView(messageItem: MessageModel(id: 1, uid: "1", text: "https://blog.wskfz.com/usr/uploads/2018/06/1718629805.png", desp: "", type: "image", pushkey_name: "Key2", created_at: "2022-01-08T18:00:48.000000Z")){}
MessageItemView(messageItem: MessageModel(id: 1, uid: "1", text: "*MarkDown*的**效果**", desp: "*MarkDown*的**效果**", type: "markdown", pushkey_name: "Key", created_at: "2021-12-28T13:44:48.000000Z")){}
MessageItemView(messageItem: MessageModel(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")){}
""", desp: "", type: "markdown", pushkey_name: "3", created_at: "2021-12-28T13:44:48.000000Z")){}
Spacer()
}
}

View File

@ -10,7 +10,11 @@ import SwiftUI
///
struct MessageListView: View {
@EnvironmentObject private var store: AppState
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \MessageModel.created_at, ascending: false)], animation: .default)
private var messages: FetchedResults<MessageModel>
var body: some View {
BaseNavigationView(title: "消息") {
ScrollView {
@ -18,15 +22,15 @@ struct MessageListView: View {
if store.isShowTestPush {
TestPushView()
}
ForEach(store.messages) { messageItem in
ForEach(messages) { messageItem in
MessageItemView(messageItem: messageItem) {
store.messages.removeAll { _messageItem in
_messageItem.id == messageItem.id
}
HToast.showSuccess("已删除")
let id = messageItem.id
viewContext.delete(messageItem)
try? viewContext.save()
HToast.showSuccess(NSLocalizedString("已删除", comment: "删除设备/Key/消息时提示"))
Task {
do {
_ = try await HttpRequest.rmMessage(id: messageItem.id)
_ = try await HttpRequest.rmMessage(id: Int(id))
} catch {
}
@ -47,7 +51,8 @@ struct MessageListView: View {
}
.onAppear {
Task {
store.messages = try await HttpRequest.getMessages().messages
let messageItems = try await HttpRequest.getMessages().messages
try MessageModel.saveAndUpdate(messageItems: messageItems)
}
}
}
@ -65,7 +70,7 @@ struct TestPushView: View {
Button("推送测试") {
print("点击推送测试")
if testText.isEmpty {
HToast.showError("推送失败, 请先输入推送内容")
HToast.showError(NSLocalizedString("推送失败, 请先输入推送内容", comment: ""))
return
}
Task {
@ -75,13 +80,13 @@ struct TestPushView: View {
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
HToast.showSuccess(NSLocalizedString("推送成功", comment: ""))
let messageItems = try await HttpRequest.getMessages().messages
withAnimation(.easeOut) {
store.messages = messages
try? MessageModel.saveAndUpdate(messageItems: messageItems)
}
} else {
HToast.showError("推送失败, 请先添加一个Key")
HToast.showError(NSLocalizedString("推送失败, 请先添加一个Key", comment: ""))
}
}
}

View File

@ -15,17 +15,17 @@ struct SettingsView: View {
var body: some View {
BaseNavigationView(title: "设置") {
VStack {
SettingsItemView(title: "登录为 \(store.userInfo?.name ?? "--")", button: "退出") {
SettingsItemView(title: NSLocalizedString("登录为", comment: "") + " \(store.userInfo?.name ?? "--")", button: NSLocalizedString("退出", comment: "退出登录按钮上的文字")) {
store.token = ""
}
.padding(EdgeInsets(top: 18, leading: 20, bottom: 0, trailing: 20))
SettingsItemView(title: "自定义服务器", button: "扫码") {
SettingsItemView(title: NSLocalizedString("自定义服务器", comment: ""), button: NSLocalizedString("扫码", comment: "")) {
}
.disabled(true)
.padding(EdgeInsets(top: 18, leading: 20, bottom: 0, trailing: 20))
SettingsItemView(title: "喜欢PushDeer?", button: "评分") {
SettingsItemView(title: NSLocalizedString("喜欢PushDeer?", comment: ""), button: NSLocalizedString("评分", comment: "")) {
let urlStr = "itms-apps://itunes.apple.com/app/id\(1596771139)?action=write-review"
UIApplication.shared.open(URL(string: urlStr)!, options: [:], completionHandler: nil)
// , 3,

View File

@ -0,0 +1,6 @@
/* Privacy - Local Network Usage Description */
"NSLocalNetworkUsageDescription" = "APP needs access to the local network for debugging (only when developing)";
/* Privacy - Photo Library Additions Usage Description */
"NSPhotoLibraryAddUsageDescription" = "APP provides you with the function of saving pictures to albums";

View File

@ -0,0 +1,111 @@
/* 在设备列表中标识当前设备 */
"(当前设备)" = "(current device)";
/* No comment provided by engineer. */
"保存成功" = "Saved successfully";
/* No comment provided by engineer. */
"保存失败" = "Saved failed";
/* No comment provided by engineer. */
"保存图片" = "Save picture";
/* No comment provided by engineer. */
"标题" = "Title";
/* token失效时提示 */
"登录过期" = "Login expired";
/* AppleId登录失败时提示 */
"登录失败" = "Login failed";
/* No comment provided by engineer. */
"登录为" = "Login as";
/* 复制按钮的标题 */
"复制" = "Copy";
/* 接口报错时提示 */
"接口报错" = "Interface error";
/* No comment provided by engineer. */
"拷贝图片" = "Copy image";
/* No comment provided by engineer. */
"内容" = "Content";
/* No comment provided by engineer. */
"评分" = "Score";
/* No comment provided by engineer. */
"扫码" = "Scan";
/* No comment provided by engineer. */
"设备" = "Devices";
/* No comment provided by engineer. */
"设置" = "Settings";
/* No comment provided by engineer. */
"输入设备名称" = "Enter device name";
/* No comment provided by engineer. */
"输入key名称" = "Enter key name";
/* No comment provided by engineer. */
"图片未加载成功" = "Image did not load successfully";
/* No comment provided by engineer. */
"推送测试" = "Push Test";
/* No comment provided by engineer. */
"推送成功" = "Push successfully";
/* No comment provided by engineer. */
"推送失败, 请先输入推送内容" = "Push failed, please enter push content first";
/* No comment provided by engineer. */
"推送失败, 请先添加一个Key" = "Push failed, please add a Key first";
/* 退出登录按钮上的文字 */
"退出" = "Logout";
/* No comment provided by engineer. */
"喜欢PushDeer?" = "Like PushDeer?";
/* No comment provided by engineer. */
"消息" = "Messages";
/* No comment provided by engineer. */
"已复制" = "Copied successfully";
/* No comment provided by engineer. */
"已拷贝" = "Copied successfully";
/* 删除设备/Key/消息时提示 */
"已删除" = "Deleted successfully";
/* No comment provided by engineer. */
"已添加当前设备" = "Current device added";
/* No comment provided by engineer. */
"已添加过当前设备" = "The current device has been added";
/* No comment provided by engineer. */
"已添加新Key" = "A new key has been added";
/* No comment provided by engineer. */
"已修改设备名称" = "Device name modified";
/* No comment provided by engineer. */
"已修改key名称" = "The key name has been modified";
/* 已重置key的提示 */
"已重置" = "Reset successfully";
/* 重置key的按钮标题 */
"重置" = "Reset";
/* No comment provided by engineer. */
"自定义服务器" = "Custom server";

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -1,19 +0,0 @@
//
// PushDeerClipApp.swift
// PushDeerClip
//
// Created by HEXT on 2021/12/30.
//
import SwiftUI
@main
struct PushDeerClipApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView().environmentObject(AppState.shared)
}
}
}

View File

@ -0,0 +1,6 @@
/* Privacy - Local Network Usage Description */
"NSLocalNetworkUsageDescription" = "APP needs access to the local network for debugging (only when developing)";
/* Privacy - Photo Library Additions Usage Description */
"NSPhotoLibraryAddUsageDescription" = "APP provides you with the function of saving pictures to albums";