mirror of
https://github.com/easychen/pushdeer.git
synced 2025-01-08 22:15:57 +08:00
Merge pull request #13 from Hext123/main
新增: 消息本地缓存 / 国际化(英文/中文) / 其它细节优化
This commit is contained in:
commit
3d76f0dbec
@ -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
|
||||
|
@ -35,6 +35,6 @@ SPEC CHECKSUMS:
|
||||
SDWebImage: 4dc3e42d9ec0c1028b960a33ac6b637bb432207b
|
||||
SDWebImageSwiftUI: 8a3923c95108312b03a599ec1498754af55a6819
|
||||
|
||||
PODFILE CHECKSUM: e462e86a9cce18b92c573f662ef405e7091cd912
|
||||
PODFILE CHECKSUM: 06aae1de50f9c1a188e69787835ec8718dd7d543
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
|
@ -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 */;
|
||||
}
|
||||
|
@ -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>
|
@ -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]
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
87
ios/PushDeer-iOS/PushDeer/Model/MessageModel.swift
Normal file
87
ios/PushDeer-iOS/PushDeer/Model/MessageModel.swift
Normal 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()
|
||||
}
|
||||
}
|
@ -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>
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
39
ios/PushDeer-iOS/PushDeer/Service/Persistence.swift
Normal file
39
ios/PushDeer-iOS/PushDeer/Service/Persistence.swift
Normal 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
|
||||
}
|
||||
}
|
@ -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):
|
||||
|
@ -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(
|
||||
|
@ -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: {
|
||||
|
||||
})
|
||||
|
47
ios/PushDeer-iOS/PushDeer/View/Common/EditableText.swift
Normal file
47
ios/PushDeer-iOS/PushDeer/View/Common/EditableText.swift
Normal 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
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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: ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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次, 用户还可以在系统设置里面关
|
||||
|
6
ios/PushDeer-iOS/PushDeer/en.lproj/InfoPlist.strings
Normal file
6
ios/PushDeer-iOS/PushDeer/en.lproj/InfoPlist.strings
Normal 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";
|
||||
|
111
ios/PushDeer-iOS/PushDeer/en.lproj/Localizable.strings
Normal file
111
ios/PushDeer-iOS/PushDeer/en.lproj/Localizable.strings
Normal 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";
|
||||
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1 @@
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
6
ios/PushDeer-iOS/PushDeerClip/en.lproj/InfoPlist.strings
Normal file
6
ios/PushDeer-iOS/PushDeerClip/en.lproj/InfoPlist.strings
Normal 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";
|
||||
|
@ -0,0 +1 @@
|
||||
|
Loading…
Reference in New Issue
Block a user