diff --git a/ios/PushDeer-iOS/Notification/Info.plist b/ios/PushDeer-iOS/Notification/Info.plist new file mode 100644 index 0000000..57421eb --- /dev/null +++ b/ios/PushDeer-iOS/Notification/Info.plist @@ -0,0 +1,13 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.usernotifications.service + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).NotificationService + + + diff --git a/ios/PushDeer-iOS/Notification/Notification.entitlements b/ios/PushDeer-iOS/Notification/Notification.entitlements new file mode 100644 index 0000000..ee95ab7 --- /dev/null +++ b/ios/PushDeer-iOS/Notification/Notification.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + diff --git a/ios/PushDeer-iOS/Notification/NotificationService.swift b/ios/PushDeer-iOS/Notification/NotificationService.swift new file mode 100644 index 0000000..2a84990 --- /dev/null +++ b/ios/PushDeer-iOS/Notification/NotificationService.swift @@ -0,0 +1,39 @@ +// +// NotificationService.swift +// Notification +// +// Created by HEXT on 2022/4/19. +// + +import UserNotifications +import WidgetKit + +class NotificationService: UNNotificationServiceExtension { + + var contentHandler: ((UNNotificationContent) -> Void)? + var bestAttemptContent: UNMutableNotificationContent? + + override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { + self.contentHandler = contentHandler + bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) + + if let bestAttemptContent = bestAttemptContent { + // Modify the notification content here... + // bestAttemptContent.title = "\(bestAttemptContent.title) [modified]" + + contentHandler(bestAttemptContent) + } + + // 刷新所有桌面小部件 + WidgetCenter.shared.reloadAllTimelines() + } + + override func serviceExtensionTimeWillExpire() { + // Called just before the extension will be terminated by the system. + // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. + if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { + contentHandler(bestAttemptContent) + } + } + +} diff --git a/ios/PushDeer-iOS/Podfile b/ios/PushDeer-iOS/Podfile index c3582aa..2bc32c5 100644 --- a/ios/PushDeer-iOS/Podfile +++ b/ios/PushDeer-iOS/Podfile @@ -28,6 +28,11 @@ target 'PushDeerClip' do end +target 'PushDeerWidgetExtension' do + # Pods for PushDeerWidgetExtension + pod 'Moya', '~> 15.0' +end + # define unsupported pods def unsupported_pods # macCatalyst 不支持的库 diff --git a/ios/PushDeer-iOS/Podfile.lock b/ios/PushDeer-iOS/Podfile.lock index 003a15d..fe5d5e2 100644 --- a/ios/PushDeer-iOS/Podfile.lock +++ b/ios/PushDeer-iOS/Podfile.lock @@ -47,6 +47,6 @@ SPEC CHECKSUMS: WechatOpenSDK: 6a4d1436c15b3b5fe2a0bd383f3046010186da44 WoodPeckeriOS: 12ec7f38c695e51cd94a476228888dfe85d9d916 -PODFILE CHECKSUM: 1b349626994062a8291e3db07d3dbf087894c4d2 +PODFILE CHECKSUM: 42e3d8abd976589c1043ff9f9e864c275a490160 COCOAPODS: 1.11.2 diff --git a/ios/PushDeer-iOS/PushDeer.xcodeproj/project.pbxproj b/ios/PushDeer-iOS/PushDeer.xcodeproj/project.pbxproj index f5eda93..a335ba2 100644 --- a/ios/PushDeer-iOS/PushDeer.xcodeproj/project.pbxproj +++ b/ios/PushDeer-iOS/PushDeer.xcodeproj/project.pbxproj @@ -7,7 +7,21 @@ objects = { /* Begin PBXBuildFile section */ + 1C63523789DA4965F9F87DB8 /* Pods_PushDeerWidgetExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F8DCAE5BD814D7C0D2F2722 /* Pods_PushDeerWidgetExtension.framework */; }; 4812F19BB0BFEFE089BC253E /* Pods_PushDeerClip.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E03C2088F4CD9F4C0848E1A5 /* Pods_PushDeerClip.framework */; }; + 5206008727CF749E00188431 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5206008627CF749E00188431 /* WidgetKit.framework */; }; + 5206008927CF749E00188431 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5206008827CF749E00188431 /* SwiftUI.framework */; }; + 5206008C27CF749E00188431 /* PushDeerWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5206008B27CF749E00188431 /* PushDeerWidget.swift */; }; + 5206008F27CF749F00188431 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5206008E27CF749F00188431 /* Assets.xcassets */; }; + 5206009127CF749F00188431 /* PushDeerWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 5206008D27CF749E00188431 /* PushDeerWidget.intentdefinition */; }; + 5206009227CF749F00188431 /* PushDeerWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 5206008D27CF749E00188431 /* PushDeerWidget.intentdefinition */; }; + 5206009527CF749F00188431 /* PushDeerWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 5206008527CF749E00188431 /* PushDeerWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 5206009D27CF74C100188431 /* HttpRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450F412784943F003652D8 /* HttpRequest.swift */; }; + 5206009E27CF76BC00188431 /* PushDeerApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450F3727848243003652D8 /* PushDeerApi.swift */; }; + 5206009F27CF76C400188431 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450F3E2784923D003652D8 /* Result.swift */; }; + 520600A027CF76F900188431 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450F3A278491F8003652D8 /* AppState.swift */; }; + 520600A127CF770600188431 /* Env.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243372627A6D86200190D00 /* Env.swift */; }; + 520600A627D0AE2300188431 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB90B22778DA4E0048E0ED /* Line.swift */; }; 52163EB327773F8400594190 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52163EB227773F8400594190 /* MainView.swift */; }; 52163EB52777413B00594190 /* MessageListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52163EB42777413B00594190 /* MessageListView.swift */; }; 52163EB72777415F00594190 /* DeviceListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52163EB62777415F00594190 /* DeviceListView.swift */; }; @@ -71,6 +85,8 @@ 52B8CF86277E0C12004CB680 /* BaseNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB90AD2778AFD60048E0ED /* BaseNavigationView.swift */; }; 52B8CF87277E0C5C004CB680 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5292F4FC2776BC7A00B9A7BB /* Assets.xcassets */; }; 52B8FB8727F36F9D00C29D13 /* BetterSafariView in Frameworks */ = {isa = PBXBuildFile; productRef = 52B8FB8627F36F9D00C29D13 /* BetterSafariView */; }; + 52C4B434280DC28D009817EA /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C4B433280DC28D009817EA /* NotificationService.swift */; }; + 52C4B438280DC28D009817EA /* Notification.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 52C4B431280DC28D009817EA /* Notification.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 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 */; }; @@ -91,6 +107,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 5206009327CF749F00188431 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5292F4ED2776BC7900B9A7BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5206008427CF749E00188431; + remoteInfo = PushDeerWidgetExtension; + }; 52B8CF71277E0B46004CB680 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 5292F4ED2776BC7900B9A7BB /* Project object */; @@ -98,9 +121,28 @@ remoteGlobalIDString = 52B8CF63277E0B44004CB680; remoteInfo = PushDeerClip; }; + 52C4B436280DC28D009817EA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5292F4ED2776BC7900B9A7BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 52C4B430280DC28D009817EA; + remoteInfo = Notification; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 5206009727CF749F00188431 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 5206009527CF749F00188431 /* PushDeerWidgetExtension.appex in Embed App Extensions */, + 52C4B438280DC28D009817EA /* Notification.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; 52B8CF77277E0B46004CB680 /* Embed App Clips */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -115,6 +157,15 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 5206008527CF749E00188431 /* PushDeerWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PushDeerWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 5206008627CF749E00188431 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + 5206008827CF749E00188431 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + 5206008B27CF749E00188431 /* PushDeerWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushDeerWidget.swift; sourceTree = ""; }; + 5206008D27CF749E00188431 /* PushDeerWidget.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = PushDeerWidget.intentdefinition; sourceTree = ""; }; + 5206008E27CF749F00188431 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 5206009027CF749F00188431 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 520600A427D085A100188431 /* PushDeerWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PushDeerWidgetExtension.entitlements; sourceTree = ""; }; + 520600A527D085C200188431 /* PushDeerWidgetExtension-SelfHosted.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "PushDeerWidgetExtension-SelfHosted.entitlements"; sourceTree = ""; }; 52163EB227773F8400594190 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; 52163EB42777413B00594190 /* MessageListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListView.swift; sourceTree = ""; }; 52163EB62777415F00594190 /* DeviceListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceListView.swift; sourceTree = ""; }; @@ -152,6 +203,10 @@ 52B8CF6F277E0B46004CB680 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52B8CF70277E0B46004CB680 /* PushDeerClip.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PushDeerClip.entitlements; sourceTree = ""; }; 52BE373227C236DD004AA630 /* PushDeer-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PushDeer-Bridging-Header.h"; sourceTree = ""; }; + 52C4B431280DC28D009817EA /* Notification.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Notification.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 52C4B433280DC28D009817EA /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; + 52C4B435280DC28D009817EA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 52C4B439280DC28D009817EA /* Notification.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Notification.entitlements; sourceTree = ""; }; 52E317D8279305BB000B8BB1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 52E317DB279305BB000B8BB1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 52E317DE279305BB000B8BB1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -169,18 +224,33 @@ 52F40D2E277CA05600766C24 /* MessageItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageItemView.swift; sourceTree = ""; }; 52FB1FEB27CA9D7300367DE0 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = ""; }; 69F56B2711ED98819D474BE3 /* Pods-PushDeerClip.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerClip.debug.xcconfig"; path = "Target Support Files/Pods-PushDeerClip/Pods-PushDeerClip.debug.xcconfig"; sourceTree = ""; }; + 7F8DCAE5BD814D7C0D2F2722 /* Pods_PushDeerWidgetExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PushDeerWidgetExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8B9D658D778AE868A0E052A8 /* Pods-PushDeer.debug-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeer.debug-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeer/Pods-PushDeer.debug-selfhosted.xcconfig"; sourceTree = ""; }; + 8CF87A70A3872FDE613FD228 /* Pods-PushDeerWidgetExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerWidgetExtension.release.xcconfig"; path = "Target Support Files/Pods-PushDeerWidgetExtension/Pods-PushDeerWidgetExtension.release.xcconfig"; sourceTree = ""; }; 9CC775BE0326BF31C6FACF06 /* Pods-PushDeerClip.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerClip.release.xcconfig"; path = "Target Support Files/Pods-PushDeerClip/Pods-PushDeerClip.release.xcconfig"; sourceTree = ""; }; + B3763489EDE07E6970B0F8E5 /* Pods-PushDeerWidgetExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerWidgetExtension.debug.xcconfig"; path = "Target Support Files/Pods-PushDeerWidgetExtension/Pods-PushDeerWidgetExtension.debug.xcconfig"; sourceTree = ""; }; B796E2A583611F7B6DC34726 /* Pods-PushDeerClip.release-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerClip.release-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeerClip/Pods-PushDeerClip.release-selfhosted.xcconfig"; sourceTree = ""; }; CB57B55699850AD493C774D0 /* Pods-PushDeerClip.debug-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerClip.debug-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeerClip/Pods-PushDeerClip.debug-selfhosted.xcconfig"; sourceTree = ""; }; CCCE1F6E56B157872E2C755F /* Pods-PushDeer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeer.release.xcconfig"; path = "Target Support Files/Pods-PushDeer/Pods-PushDeer.release.xcconfig"; sourceTree = ""; }; CE3005BD875FC9819A92466C /* Pods-PushDeer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeer.debug.xcconfig"; path = "Target Support Files/Pods-PushDeer/Pods-PushDeer.debug.xcconfig"; sourceTree = ""; }; + D612A216E0469D1A050C3523 /* Pods-PushDeerWidgetExtension.release-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerWidgetExtension.release-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeerWidgetExtension/Pods-PushDeerWidgetExtension.release-selfhosted.xcconfig"; sourceTree = ""; }; E03C2088F4CD9F4C0848E1A5 /* Pods_PushDeerClip.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PushDeerClip.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E380A18349DE4D26071E913E /* Pods_PushDeer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PushDeer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E45C830227D3A7C7E965F94B /* Pods-PushDeerWidgetExtension.debug-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerWidgetExtension.debug-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeerWidgetExtension/Pods-PushDeerWidgetExtension.debug-selfhosted.xcconfig"; sourceTree = ""; }; E60A7D4CA1149E1DBC55C672 /* Pods-PushDeer.release-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeer.release-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeer/Pods-PushDeer.release-selfhosted.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 5206008227CF749E00188431 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5206008927CF749E00188431 /* SwiftUI.framework in Frameworks */, + 5206008727CF749E00188431 /* WidgetKit.framework in Frameworks */, + 1C63523789DA4965F9F87DB8 /* Pods_PushDeerWidgetExtension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 5292F4F22776BC7900B9A7BB /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -201,6 +271,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 52C4B42E280DC28D009817EA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -215,10 +292,27 @@ E60A7D4CA1149E1DBC55C672 /* Pods-PushDeer.release-selfhosted.xcconfig */, CB57B55699850AD493C774D0 /* Pods-PushDeerClip.debug-selfhosted.xcconfig */, B796E2A583611F7B6DC34726 /* Pods-PushDeerClip.release-selfhosted.xcconfig */, + B3763489EDE07E6970B0F8E5 /* Pods-PushDeerWidgetExtension.debug.xcconfig */, + E45C830227D3A7C7E965F94B /* Pods-PushDeerWidgetExtension.debug-selfhosted.xcconfig */, + 8CF87A70A3872FDE613FD228 /* Pods-PushDeerWidgetExtension.release.xcconfig */, + D612A216E0469D1A050C3523 /* Pods-PushDeerWidgetExtension.release-selfhosted.xcconfig */, ); path = Pods; sourceTree = ""; }; + 5206008A27CF749E00188431 /* PushDeerWidget */ = { + isa = PBXGroup; + children = ( + 5206008B27CF749E00188431 /* PushDeerWidget.swift */, + 5206008D27CF749E00188431 /* PushDeerWidget.intentdefinition */, + 5206008E27CF749F00188431 /* Assets.xcassets */, + 5206009027CF749F00188431 /* Info.plist */, + 520600A427D085A100188431 /* PushDeerWidgetExtension.entitlements */, + 520600A527D085C200188431 /* PushDeerWidgetExtension-SelfHosted.entitlements */, + ); + path = PushDeerWidget; + sourceTree = ""; + }; 52450F362784822C003652D8 /* Service */ = { isa = PBXGroup; children = ( @@ -246,6 +340,8 @@ children = ( 5292F4F72776BC7900B9A7BB /* PushDeer */, 52B8CF65277E0B44004CB680 /* PushDeerClip */, + 5206008A27CF749E00188431 /* PushDeerWidget */, + 52C4B432280DC28D009817EA /* Notification */, 5292F4F62776BC7900B9A7BB /* Products */, 17D35B157765D96FC4DA6C39 /* Pods */, 78FEAD2568FB92808C44E85A /* Frameworks */, @@ -258,6 +354,8 @@ children = ( 5292F4F52776BC7900B9A7BB /* PushDeer.app */, 52B8CF64277E0B44004CB680 /* PushDeerClip.app */, + 5206008527CF749E00188431 /* PushDeerWidgetExtension.appex */, + 52C4B431280DC28D009817EA /* Notification.appex */, ); name = Products; sourceTree = ""; @@ -327,6 +425,16 @@ path = "Preview Content"; sourceTree = ""; }; + 52C4B432280DC28D009817EA /* Notification */ = { + isa = PBXGroup; + children = ( + 52C4B439280DC28D009817EA /* Notification.entitlements */, + 52C4B433280DC28D009817EA /* NotificationService.swift */, + 52C4B435280DC28D009817EA /* Info.plist */, + ); + path = Notification; + sourceTree = ""; + }; 52EB90B12778D9F90048E0ED /* Common */ = { isa = PBXGroup; children = ( @@ -363,6 +471,9 @@ children = ( E380A18349DE4D26071E913E /* Pods_PushDeer.framework */, E03C2088F4CD9F4C0848E1A5 /* Pods_PushDeerClip.framework */, + 5206008627CF749E00188431 /* WidgetKit.framework */, + 5206008827CF749E00188431 /* SwiftUI.framework */, + 7F8DCAE5BD814D7C0D2F2722 /* Pods_PushDeerWidgetExtension.framework */, ); name = Frameworks; sourceTree = ""; @@ -370,6 +481,26 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 5206008427CF749E00188431 /* PushDeerWidgetExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5206009C27CF749F00188431 /* Build configuration list for PBXNativeTarget "PushDeerWidgetExtension" */; + buildPhases = ( + 97FE7C51BE9FCE0E275A4CA3 /* [CP] Check Pods Manifest.lock */, + 5206008127CF749E00188431 /* Sources */, + 5206008227CF749E00188431 /* Frameworks */, + 5206008327CF749E00188431 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PushDeerWidgetExtension; + packageProductDependencies = ( + ); + productName = PushDeerWidgetExtension; + productReference = 5206008527CF749E00188431 /* PushDeerWidgetExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; 5292F4F42776BC7900B9A7BB /* PushDeer */ = { isa = PBXNativeTarget; buildConfigurationList = 5292F5032776BC7A00B9A7BB /* Build configuration list for PBXNativeTarget "PushDeer" */; @@ -380,11 +511,14 @@ 5292F4F32776BC7900B9A7BB /* Resources */, 52B8CF77277E0B46004CB680 /* Embed App Clips */, D5C7FAC44EC37CBCD945E0F2 /* [CP] Embed Pods Frameworks */, + 5206009727CF749F00188431 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( 52B8CF72277E0B46004CB680 /* PBXTargetDependency */, + 5206009427CF749F00188431 /* PBXTargetDependency */, + 52C4B437280DC28D009817EA /* PBXTargetDependency */, ); name = PushDeer; packageProductDependencies = ( @@ -418,6 +552,23 @@ productReference = 52B8CF64277E0B44004CB680 /* PushDeerClip.app */; productType = "com.apple.product-type.application.on-demand-install-capable"; }; + 52C4B430280DC28D009817EA /* Notification */ = { + isa = PBXNativeTarget; + buildConfigurationList = 52C4B43E280DC28D009817EA /* Build configuration list for PBXNativeTarget "Notification" */; + buildPhases = ( + 52C4B42D280DC28D009817EA /* Sources */, + 52C4B42E280DC28D009817EA /* Frameworks */, + 52C4B42F280DC28D009817EA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Notification; + productName = Notification; + productReference = 52C4B431280DC28D009817EA /* Notification.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -425,9 +576,12 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1320; + LastSwiftUpdateCheck = 1330; LastUpgradeCheck = 1320; TargetAttributes = { + 5206008427CF749E00188431 = { + CreatedOnToolsVersion = 13.2.1; + }; 5292F4F42776BC7900B9A7BB = { CreatedOnToolsVersion = 13.2.1; LastSwiftMigration = 1320; @@ -436,6 +590,9 @@ CreatedOnToolsVersion = 13.2.1; LastSwiftMigration = 1320; }; + 52C4B430280DC28D009817EA = { + CreatedOnToolsVersion = 13.3.1; + }; }; }; buildConfigurationList = 5292F4F02776BC7900B9A7BB /* Build configuration list for PBXProject "PushDeer" */; @@ -458,11 +615,21 @@ targets = ( 5292F4F42776BC7900B9A7BB /* PushDeer */, 52B8CF63277E0B44004CB680 /* PushDeerClip */, + 5206008427CF749E00188431 /* PushDeerWidgetExtension */, + 52C4B430280DC28D009817EA /* Notification */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 5206008327CF749E00188431 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5206008F27CF749F00188431 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 5292F4F32776BC7900B9A7BB /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -485,6 +652,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 52C4B42F280DC28D009817EA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -527,6 +701,28 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PushDeerClip/Pods-PushDeerClip-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + 97FE7C51BE9FCE0E275A4CA3 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-PushDeerWidgetExtension-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; D5C7FAC44EC37CBCD945E0F2 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -569,6 +765,21 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 5206008127CF749E00188431 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5206009127CF749F00188431 /* PushDeerWidget.intentdefinition in Sources */, + 5206009F27CF76C400188431 /* Result.swift in Sources */, + 5206008C27CF749E00188431 /* PushDeerWidget.swift in Sources */, + 520600A027CF76F900188431 /* AppState.swift in Sources */, + 5206009E27CF76BC00188431 /* PushDeerApi.swift in Sources */, + 520600A627D0AE2300188431 /* Line.swift in Sources */, + 5206009D27CF74C100188431 /* HttpRequest.swift in Sources */, + 520600A127CF770600188431 /* Env.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 5292F4F12776BC7900B9A7BB /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -577,6 +788,7 @@ 52FB1FEC27CA9D7300367DE0 /* SafariView.swift in Sources */, 523150DC2778762B00941EDC /* DeviceItemView.swift in Sources */, 523150D9277875FB00941EDC /* DeletableView.swift in Sources */, + 5206009227CF749F00188431 /* PushDeerWidget.intentdefinition in Sources */, 52163EBB277741AC00594190 /* SettingsView.swift in Sources */, 5242C872278C8CBB00FDB27E /* EditableText.swift in Sources */, 52163EB327773F8400594190 /* MainView.swift in Sources */, @@ -649,15 +861,33 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 52C4B42D280DC28D009817EA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 52C4B434280DC28D009817EA /* NotificationService.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 5206009427CF749F00188431 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5206008427CF749E00188431 /* PushDeerWidgetExtension */; + targetProxy = 5206009327CF749F00188431 /* PBXContainerItemProxy */; + }; 52B8CF72277E0B46004CB680 /* PBXTargetDependency */ = { isa = PBXTargetDependency; platformFilter = ios; target = 52B8CF63277E0B44004CB680 /* PushDeerClip */; targetProxy = 52B8CF71277E0B46004CB680 /* PBXContainerItemProxy */; }; + 52C4B437280DC28D009817EA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 52C4B430280DC28D009817EA /* Notification */; + targetProxy = 52C4B436280DC28D009817EA /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -691,6 +921,130 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 5206009827CF749F00188431 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B3763489EDE07E6970B0F8E5 /* Pods-PushDeerWidgetExtension.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_ENTITLEMENTS = PushDeerWidget/PushDeerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = HUJ6HAE4VU; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = PushDeerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = PushDeerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.2; + PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.app.ios.PushDeerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 5206009927CF749F00188431 /* Debug-SelfHosted */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E45C830227D3A7C7E965F94B /* Pods-PushDeerWidgetExtension.debug-selfhosted.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_ENTITLEMENTS = "PushDeerWidget/PushDeerWidgetExtension-SelfHosted.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = HUJ6HAE4VU; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = PushDeerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = PushDeerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.2; + PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.self.ios.PushDeerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Debug-SelfHosted"; + }; + 5206009A27CF749F00188431 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8CF87A70A3872FDE613FD228 /* Pods-PushDeerWidgetExtension.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_ENTITLEMENTS = PushDeerWidget/PushDeerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = HUJ6HAE4VU; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = PushDeerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = PushDeerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.2; + PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.app.ios.PushDeerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 5206009B27CF749F00188431 /* Release-SelfHosted */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D612A216E0469D1A050C3523 /* Pods-PushDeerWidgetExtension.release-selfhosted.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_ENTITLEMENTS = "PushDeerWidget/PushDeerWidgetExtension-SelfHosted.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = HUJ6HAE4VU; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = PushDeerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = PushDeerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.2; + PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.self.ios.PushDeerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Release-SelfHosted"; + }; 524E99E827B3DDF000292396 /* Debug-SelfHosted */ = { isa = XCBuildConfiguration; buildSettings = { @@ -758,6 +1112,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 8B9D658D778AE868A0E052A8 /* Pods-PushDeer.debug-selfhosted.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-SH"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; @@ -901,6 +1256,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = E60A7D4CA1149E1DBC55C672 /* Pods-PushDeer.release-selfhosted.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-SH"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; @@ -1102,6 +1458,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = CE3005BD875FC9819A92466C /* Pods-PushDeer.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; @@ -1143,6 +1500,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = CCCE1F6E56B157872E2C755F /* Pods-PushDeer.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; @@ -1266,9 +1624,132 @@ }; name = Release; }; + 52C4B43A280DC28D009817EA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = Notification/Notification.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = HUJ6HAE4VU; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Notification/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Notification; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.2; + PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.app.ios.Notification; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 52C4B43B280DC28D009817EA /* Debug-SelfHosted */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = Notification/Notification.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = HUJ6HAE4VU; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Notification/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Notification; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.2; + PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.self.ios.Notification; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Debug-SelfHosted"; + }; + 52C4B43C280DC28D009817EA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = Notification/Notification.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = HUJ6HAE4VU; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Notification/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Notification; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.2; + PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.app.ios.Notification; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 52C4B43D280DC28D009817EA /* Release-SelfHosted */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = Notification/Notification.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = HUJ6HAE4VU; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Notification/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Notification; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.2; + PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.self.ios.Notification; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Release-SelfHosted"; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 5206009C27CF749F00188431 /* Build configuration list for PBXNativeTarget "PushDeerWidgetExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5206009827CF749F00188431 /* Debug */, + 5206009927CF749F00188431 /* Debug-SelfHosted */, + 5206009A27CF749F00188431 /* Release */, + 5206009B27CF749F00188431 /* Release-SelfHosted */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 5292F4F02776BC7900B9A7BB /* Build configuration list for PBXProject "PushDeer" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -1302,6 +1783,17 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 52C4B43E280DC28D009817EA /* Build configuration list for PBXNativeTarget "Notification" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 52C4B43A280DC28D009817EA /* Debug */, + 52C4B43B280DC28D009817EA /* Debug-SelfHosted */, + 52C4B43C280DC28D009817EA /* Release */, + 52C4B43D280DC28D009817EA /* Release-SelfHosted */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/ios/PushDeer-iOS/PushDeer/Env.swift b/ios/PushDeer-iOS/PushDeer/Env.swift index 414a088..920d034 100644 --- a/ios/PushDeer-iOS/PushDeer/Env.swift +++ b/ios/PushDeer-iOS/PushDeer/Env.swift @@ -27,5 +27,13 @@ struct Env { static let wxUniversalLink = "https://vip.pushdeer.com/app/" /// PushDeer 官网地址 static let officialWebsite = "https://www.pushdeer.com" + /// 共享组名, 使用它来访问 App Clip Widget 共享数据 + static let appGroupId: String = { + #if SELFHOSTED + return "group.com.pushdeer.self.ios" + #else + return "group.com.pushdeer.app.ios" + #endif + }() } diff --git a/ios/PushDeer-iOS/PushDeer/Model/MessageModel.swift b/ios/PushDeer-iOS/PushDeer/Model/MessageModel.swift index 4ceaecb..23b4f4a 100644 --- a/ios/PushDeer-iOS/PushDeer/Model/MessageModel.swift +++ b/ios/PushDeer-iOS/PushDeer/Model/MessageModel.swift @@ -51,6 +51,19 @@ extension MessageModel { static let _viewContext = PersistenceController.shared.container.viewContext static let _fetchRequest = MessageModel.fetchRequest() + /// 删除本地持久化的所有消息 + static func deleteAll() throws -> Void { + // let fetchRequest: NSFetchRequest = MessageModel.fetchRequest() + // let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) + // try _viewContext.execute(deleteRequest) + let fetchRequest = MessageModel.fetchRequest() + let models = try _viewContext.fetch(fetchRequest) + models.forEach { model in + _viewContext.delete(model) + } + try _viewContext.save() + } + /// 持久化保存和更新 static func saveAndUpdate(messageItems: [MessageItem]) throws -> Void { try messageItems.forEach(saveAndUpdate) diff --git a/ios/PushDeer-iOS/PushDeer/PushDeer-SelfHosted.entitlements b/ios/PushDeer-iOS/PushDeer/PushDeer-SelfHosted.entitlements index 64fe498..b5c7e16 100644 --- a/ios/PushDeer-iOS/PushDeer/PushDeer-SelfHosted.entitlements +++ b/ios/PushDeer-iOS/PushDeer/PushDeer-SelfHosted.entitlements @@ -14,6 +14,10 @@ com.apple.security.app-sandbox + com.apple.security.application-groups + + group.com.pushdeer.self.ios + com.apple.security.network.client com.apple.security.personal-information.photos-library diff --git a/ios/PushDeer-iOS/PushDeer/PushDeer.entitlements b/ios/PushDeer-iOS/PushDeer/PushDeer.entitlements index 9cc65b4..46f1c07 100644 --- a/ios/PushDeer-iOS/PushDeer/PushDeer.entitlements +++ b/ios/PushDeer-iOS/PushDeer/PushDeer.entitlements @@ -15,6 +15,10 @@ com.apple.security.app-sandbox + com.apple.security.application-groups + + group.com.pushdeer.app.ios + com.apple.security.network.client com.apple.security.personal-information.photos-library diff --git a/ios/PushDeer-iOS/PushDeer/PushDeerApp.swift b/ios/PushDeer-iOS/PushDeer/PushDeerApp.swift index dab7bc5..51e214c 100644 --- a/ios/PushDeer-iOS/PushDeer/PushDeerApp.swift +++ b/ios/PushDeer-iOS/PushDeer/PushDeerApp.swift @@ -6,6 +6,7 @@ // import SwiftUI +import WidgetKit @main struct PushDeerApp: App { @@ -39,6 +40,10 @@ struct PushDeerApp: App { } } } + .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification), perform: { _ in + // APP将要进入非活跃状态时, 刷新小部件 + WidgetCenter.shared.reloadAllTimelines() + }) .environmentObject(store) .environment(\.managedObjectContext, persistenceController.container.viewContext) } diff --git a/ios/PushDeer-iOS/PushDeer/Service/AppState.swift b/ios/PushDeer-iOS/PushDeer/Service/AppState.swift index 5d33992..96e5dc2 100644 --- a/ios/PushDeer-iOS/PushDeer/Service/AppState.swift +++ b/ios/PushDeer-iOS/PushDeer/Service/AppState.swift @@ -10,9 +10,9 @@ import AuthenticationServices class AppState: ObservableObject { /// 账号 token - @Published var token : String { + @Published var token : String = "" { didSet { - UserDefaults.standard.set(token, forKey: "PushDeer_token") + getUserDefaults().set(token, forKey: "PushDeer_token") } } /// 设备列表 @@ -22,9 +22,9 @@ class AppState: ObservableObject { /// 消息列表 // @Published var messages: [MessageItem] = [] /// 选中的 tab 下标 - @Published var tabSelectedIndex: Int { + @Published var tabSelectedIndex: Int = 2 { didSet { - UserDefaults.standard.set(tabSelectedIndex, forKey: "PushDeer_tabSelectedIndex") + getUserDefaults().set(tabSelectedIndex, forKey: "PushDeer_tabSelectedIndex") } } /// 设备推送 token @@ -32,28 +32,28 @@ class AppState: ObservableObject { /// 用户信息 @Published var userInfo: UserInfoContent? /// 是否显示测试发推送的 UI - @Published var isShowTestPush: Bool { + @Published var isShowTestPush: Bool = true { didSet { - UserDefaults.standard.set(isShowTestPush, forKey: "PushDeer_isShowTestPush") + getUserDefaults().set(isShowTestPush, forKey: "PushDeer_isShowTestPush") } } /// 是否使用内置浏览器打开链接 - @Published var isUseBuiltInBrowser: Bool { + @Published var isUseBuiltInBrowser: Bool = true { didSet { - UserDefaults.standard.set(isUseBuiltInBrowser, forKey: "PushDeer_isUseBuiltInBrowser") + getUserDefaults().set(isUseBuiltInBrowser, forKey: "PushDeer_isUseBuiltInBrowser") } } /// MarkDown BaseURL @Published var markDownBaseURL: String? { didSet { - UserDefaults.standard.set(markDownBaseURL, forKey: "PushDeer_markDownBaseURL") + getUserDefaults().set(markDownBaseURL, forKey: "PushDeer_markDownBaseURL") } } /// API endpoint - @Published var api_endpoint : String { + @Published var api_endpoint : String = "" { didSet { - UserDefaults.standard.set(api_endpoint, forKey: "PushDeer_api_endpoint") + getUserDefaults().set(api_endpoint, forKey: "PushDeer_api_endpoint") } } @@ -68,12 +68,26 @@ class AppState: ObservableObject { static let shared = AppState() private init() { - let _token = UserDefaults.standard.string(forKey: "PushDeer_token") - let _tabSelectedIndex = UserDefaults.standard.integer(forKey: "PushDeer_tabSelectedIndex") - let _isShowTestPush = UserDefaults.standard.object(forKey: "PushDeer_isShowTestPush") - let _isUseBuiltInBrowser = UserDefaults.standard.object(forKey: "PushDeer_isUseBuiltInBrowser") - let _markDownBaseURL = UserDefaults.standard.string(forKey: "PushDeer_markDownBaseURL") - let _api_endpoint = UserDefaults.standard.string(forKey: "PushDeer_api_endpoint") + reloadUserDefaults() + moveOldUserDefaults() + } + + func getUserDefaults() -> UserDefaults { + let ud = UserDefaults(suiteName: Env.appGroupId) + if let ud = ud { + return ud + } else { + return UserDefaults.standard + } + } + + func reloadUserDefaults() -> Void { + let _token = getUserDefaults().string(forKey: "PushDeer_token") + let _tabSelectedIndex = getUserDefaults().integer(forKey: "PushDeer_tabSelectedIndex") + let _isShowTestPush = getUserDefaults().object(forKey: "PushDeer_isShowTestPush") + let _isUseBuiltInBrowser = getUserDefaults().object(forKey: "PushDeer_isUseBuiltInBrowser") + let _markDownBaseURL = getUserDefaults().string(forKey: "PushDeer_markDownBaseURL") + let _api_endpoint = getUserDefaults().string(forKey: "PushDeer_api_endpoint") token = _token ?? "" tabSelectedIndex = _tabSelectedIndex isShowTestPush = _isShowTestPush as? Bool ?? true @@ -82,6 +96,35 @@ class AppState: ObservableObject { api_endpoint = _api_endpoint ?? "" } + /// 迁移老版本数据, 老版本不是存在共享组中, 需要迁移到共享组 + func moveOldUserDefaults() -> Void { + let oldUserDefaults = UserDefaults.standard + if let _token = oldUserDefaults.string(forKey: "PushDeer_token") { + oldUserDefaults.removeObject(forKey: "PushDeer_token") + token = _token + } + if let _tabSelectedIndex = oldUserDefaults.object(forKey: "PushDeer_tabSelectedIndex") as? Int { + oldUserDefaults.removeObject(forKey: "PushDeer_tabSelectedIndex") + tabSelectedIndex = _tabSelectedIndex + } + if let _isShowTestPush = oldUserDefaults.object(forKey: "PushDeer_isShowTestPush") as? Bool { + oldUserDefaults.removeObject(forKey: "PushDeer_isShowTestPush") + isShowTestPush = _isShowTestPush + } + if let _isUseBuiltInBrowser = oldUserDefaults.object(forKey: "PushDeer_isUseBuiltInBrowser") as? Bool { + oldUserDefaults.removeObject(forKey: "PushDeer_isUseBuiltInBrowser") + isUseBuiltInBrowser = _isUseBuiltInBrowser + } + if let _markDownBaseURL = oldUserDefaults.string(forKey: "PushDeer_markDownBaseURL") { + oldUserDefaults.removeObject(forKey: "PushDeer_markDownBaseURL") + markDownBaseURL = _markDownBaseURL + } + if let _api_endpoint = oldUserDefaults.string(forKey: "PushDeer_api_endpoint") { + oldUserDefaults.removeObject(forKey: "PushDeer_api_endpoint") + api_endpoint = _api_endpoint + } + } + func appleIdLogin(_ result: Result) async throws -> TokenContent { switch result { case let .success(authorization): diff --git a/ios/PushDeer-iOS/PushDeer/Service/HttpRequest.swift b/ios/PushDeer-iOS/PushDeer/Service/HttpRequest.swift index 4c0149d..1e91793 100644 --- a/ios/PushDeer-iOS/PushDeer/Service/HttpRequest.swift +++ b/ios/PushDeer-iOS/PushDeer/Service/HttpRequest.swift @@ -135,4 +135,8 @@ struct HttpRequest { static func rmMessage(id: Int) async throws -> ActionContent { return try await request(.rmMessage(token: AppState.shared.token, id: id), resultType: ActionContent.self) } + + static func rmAllMessage() async throws -> ActionContent { + return try await request(.rmAllMessage(token: AppState.shared.token), resultType: ActionContent.self) + } } diff --git a/ios/PushDeer-iOS/PushDeer/Service/PushDeerApi.swift b/ios/PushDeer-iOS/PushDeer/Service/PushDeerApi.swift index 01541af..652f6b4 100644 --- a/ios/PushDeer-iOS/PushDeer/Service/PushDeerApi.swift +++ b/ios/PushDeer-iOS/PushDeer/Service/PushDeerApi.swift @@ -40,6 +40,7 @@ enum PushDeerApi { case getMessages(token: String, limit: Int) case rmMessage(token: String, id: Int) + case rmAllMessage(token: String) } @@ -91,6 +92,8 @@ extension PushDeerApi: TargetType { return "/message/list" case .rmMessage: return "/message/remove" + case .rmAllMessage: + return "/message/clean" } } var method: Moya.Method { @@ -139,6 +142,8 @@ extension PushDeerApi: TargetType { return .requestParameters(parameters: ["token": token, "limit": limit],encoding: URLEncoding.queryString) case let .rmMessage(token, id): return .requestParameters(parameters: ["token": token, "id": id],encoding: URLEncoding.queryString) + case let .rmAllMessage(token): + return .requestParameters(parameters: ["token": token],encoding: URLEncoding.queryString) } } diff --git a/ios/PushDeer-iOS/PushDeer/View/MessageListView.swift b/ios/PushDeer-iOS/PushDeer/View/MessageListView.swift index ef0c80a..a11c664 100644 --- a/ios/PushDeer-iOS/PushDeer/View/MessageListView.swift +++ b/ios/PushDeer-iOS/PushDeer/View/MessageListView.swift @@ -15,6 +15,17 @@ struct MessageListView: View { @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \MessageModel.created_at, ascending: false)], animation: .default) private var messages: FetchedResults + @State private var showRemoveAllMessageView: Bool = false + @State private var lastDeleteTime: TimeInterval = 0 + + func recordDeleteTime() -> Void { + let currentDeleteTime = Date.timeIntervalSinceReferenceDate + if currentDeleteTime - lastDeleteTime < 60 { + showRemoveAllMessageView = true + } + lastDeleteTime = currentDeleteTime + } + var body: some View { BaseNavigationView(title: "消息") { ScrollView { @@ -28,6 +39,7 @@ struct MessageListView: View { viewContext.delete(messageItem) try? viewContext.save() HToast.showSuccess(NSLocalizedString("已删除", comment: "删除设备/Key/消息时提示")) + recordDeleteTime() Task { do { _ = try await HttpRequest.rmMessage(id: Int(id)) @@ -40,6 +52,14 @@ struct MessageListView: View { Spacer(minLength: 30) } } + .overlay( + Group { + if (showRemoveAllMessageView) { + RemoveAllMessageView{showRemoveAllMessageView = false} + } + }, + alignment: .bottom + ) .navigationBarItems(trailing: Button(action: { withAnimation(.easeOut) { store.isShowTestPush = !store.isShowTestPush @@ -115,8 +135,51 @@ struct TestPushView: View { } } +struct RemoveAllMessageView: View { + /// 取消按钮点击的回调 + let closeAction : () -> () + var body: some View { + VStack(alignment: .center, spacing: 0) { + HLine() + .stroke(Color("borderColor")) + .frame(height: 1) + HStack(spacing: 12) { + Button(NSLocalizedString("清除全部消息", comment: "")) { + Task { + do { + try MessageModel.deleteAll() + _ = try await HttpRequest.rmAllMessage() + HToast.showSuccess(NSLocalizedString("已清空", comment: "")) + self.closeAction() + } catch { + HToast.showError(error.localizedDescription) + } + } + } + .font(.system(size: 20)) + .padding(.horizontal) + .frame( height: 42) + .overlay(RoundedRectangle(cornerRadius: 4).stroke()) + .foregroundColor(Color.accentColor) + + Button(NSLocalizedString("取消", comment: "")) { + self.closeAction() + } + .font(.system(size: 20)) + .padding(.horizontal) + .frame( height: 42) + .overlay(RoundedRectangle(cornerRadius: 4).stroke()) + .foregroundColor(Color.accentColor) + } + .padding() + } + .background(Color("backgroundColor").opacity(0.9)) + } +} + struct MessageView_Previews: PreviewProvider { static var previews: some View { MessageListView() + .environmentObject(AppState.shared) } } diff --git a/ios/PushDeer-iOS/PushDeer/View/SettingsView.swift b/ios/PushDeer-iOS/PushDeer/View/SettingsView.swift index e18730e..1c3b0ea 100644 --- a/ios/PushDeer-iOS/PushDeer/View/SettingsView.swift +++ b/ios/PushDeer-iOS/PushDeer/View/SettingsView.swift @@ -21,6 +21,7 @@ struct SettingsView: View { VStack { SettingsItemView(title: NSLocalizedString("登录为", comment: "") + " " + userName(), button: NSLocalizedString("退出", comment: "退出登录按钮上的文字")) { store.token = "" + HToast.showText(NSLocalizedString("退出", comment: "退出登录按钮上的文字")) } .padding(EdgeInsets(top: 18, leading: 20, bottom: 0, trailing: 20)) diff --git a/ios/PushDeer-iOS/PushDeerClip/PushDeerClip-SelfHosted.entitlements b/ios/PushDeer-iOS/PushDeerClip/PushDeerClip-SelfHosted.entitlements index bc419bf..a8f59a6 100644 --- a/ios/PushDeer-iOS/PushDeerClip/PushDeerClip-SelfHosted.entitlements +++ b/ios/PushDeer-iOS/PushDeerClip/PushDeerClip-SelfHosted.entitlements @@ -16,5 +16,9 @@ $(AppIdentifierPrefix)com.pushdeer.self.ios + com.apple.security.application-groups + + group.com.pushdeer.self.ios + diff --git a/ios/PushDeer-iOS/PushDeerClip/PushDeerClip.entitlements b/ios/PushDeer-iOS/PushDeerClip/PushDeerClip.entitlements index a92507e..e9fb37d 100644 --- a/ios/PushDeer-iOS/PushDeerClip/PushDeerClip.entitlements +++ b/ios/PushDeer-iOS/PushDeerClip/PushDeerClip.entitlements @@ -16,5 +16,9 @@ $(AppIdentifierPrefix)com.pushdeer.app.ios + com.apple.security.application-groups + + group.com.pushdeer.app.ios + diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..f21a778 --- /dev/null +++ b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.537", + "green" : "0.278", + "red" : "0.231" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.702", + "green" : "0.365", + "red" : "0.302" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Contents.json b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/Contents.json b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/Contents.json b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/Contents.json new file mode 100644 index 0000000..d12cc1a --- /dev/null +++ b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "deer.gray.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "deer.gray@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/deer.gray.png b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/deer.gray.png new file mode 100644 index 0000000..308d2d5 Binary files /dev/null and b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/deer.gray.png differ diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/deer.gray@2x.png b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/deer.gray@2x.png new file mode 100644 index 0000000..775bd52 Binary files /dev/null and b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/deer.gray@2x.png differ diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/PushDeer-iOS/PushDeerWidget/Info.plist b/ios/PushDeer-iOS/PushDeerWidget/Info.plist new file mode 100644 index 0000000..d4e598e --- /dev/null +++ b/ios/PushDeer-iOS/PushDeerWidget/Info.plist @@ -0,0 +1,16 @@ + + + + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidget.intentdefinition b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidget.intentdefinition new file mode 100644 index 0000000..bdb4045 --- /dev/null +++ b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidget.intentdefinition @@ -0,0 +1,59 @@ + + + + + INEnums + + INIntentDefinitionModelVersion + 1.2 + INIntentDefinitionNamespace + 88xZPY + INIntentDefinitionSystemVersion + 20A294 + INIntentDefinitionToolsBuildVersion + 12A6144 + INIntentDefinitionToolsVersion + 12.0 + INIntents + + + INIntentCategory + information + INIntentDescriptionID + tVvJ9c + INIntentEligibleForWidgets + + INIntentIneligibleForSuggestions + + INIntentName + Configuration + INIntentResponse + + INIntentResponseCodes + + + INIntentResponseCodeName + success + INIntentResponseCodeSuccess + + + + INIntentResponseCodeName + failure + + + + INIntentTitle + Configuration + INIntentTitleID + gpCwrM + INIntentType + Custom + INIntentVerb + View + + + INTypes + + + diff --git a/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidget.swift b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidget.swift new file mode 100644 index 0000000..1a33d85 --- /dev/null +++ b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidget.swift @@ -0,0 +1,179 @@ +// +// PushDeerWidget.swift +// PushDeerWidget +// +// Created by HEXT on 2022/3/2. +// + +import WidgetKit +import SwiftUI +import Intents + +struct Provider: IntentTimelineProvider { + + func placeholder(in context: Context) -> SimpleEntry { + SimpleEntry(date: Date(), configuration: ConfigurationIntent()) + } + + func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) { + let entry = SimpleEntry(date: Date(), configuration: configuration) + completion(entry) + } + + func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> ()) { + Task { + let currentDate = Date() + let entryDate = Calendar.current.date(byAdding: .minute, value: 1, to: currentDate)! + var entries: [SimpleEntry] = [] + var entry = SimpleEntry(date: entryDate, configuration: configuration) + AppState.shared.reloadUserDefaults() + print("token", AppState.shared.token) + do { + let messages = try await HttpRequest.getMessages().messages + entry.messages = handleList(messages, context: context) + } catch { + + } + entries.append(entry) + let timeline = Timeline(entries: entries, policy: .atEnd) + completion(timeline) + } + } + + func handleList(_ origList: [MessageItem], context: Context) -> [MessageItem] { + var list = origList + var limit = 0 + switch context.family { + case .systemSmall, .systemMedium: + limit = 4 + default: + limit = 10 + } + if list.count <= limit + 1 { + return list; + } + list = list.prefix(limit) + [] + list.append( + MessageItem(id: -1, uid: "", text: "+其它\(origList.count - limit)条", desp: "", type: "text", pushkey_name: "", created_at: "") + ) + return list; + } +} + +struct SimpleEntry: TimelineEntry { + let date: Date + let configuration: ConfigurationIntent + var messages: [MessageItem] = placeholderList +} + +struct PushDeerWidgetEntryView : View { + var entry: Provider.Entry + + var body: some View { + HContentView { + if AppState.shared.token.isEmpty { + Text("未登录") + } else if entry.messages.isEmpty { + Text("无消息") + } else { + VStack(alignment: .leading, spacing: 5) { + ForEach(entry.messages) { + if entry.messages.first?.id != $0.id { + Divider() + } + if $0.id == -1 { + HText(text: $0.text) + .foregroundColor(.accentColor) + } else { + HText(text: $0.text) + } + } + .font(.system(size: 15)) + Spacer(minLength: 0) + } + .padding(.top, 5) + .padding() + .accentColor(Color("AccentColor")) + } + } + } +} + +struct HContentView: View { + /// 页面主体View + @ViewBuilder let contentView: Content + + @Environment(\.colorScheme) private var colorScheme + + var body: some View { + ZStack { + // VStack HStack Spacer 组合起来撑到最大 + VStack { + HStack { + Spacer() + } + Spacer() + } + contentView + } + .background( + Image("deer.gray") + .opacity(colorScheme == .dark ? 0.4 : 1), + alignment: .topTrailing + ) + } +} + +struct HText: View { + let text: String + var body: some View { + if #available(iOSApplicationExtension 15.0, *) { + Group { + if #available(iOSApplicationExtension 15.0, *) { + Text(try! AttributedString(markdown: text)) + } + } + } else { + Text(verbatim: text) + } + } +} + +@main +struct PushDeerWidget: Widget { + let kind: String = "PushDeerWidget" + + var body: some WidgetConfiguration { + IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in + PushDeerWidgetEntryView(entry: entry) + } + .configurationDisplayName("最近的PushDeer消息") + .description("这个小部件可以快捷展示你最近收到的消息.") + } +} + +struct PushDeerWidget_Previews: PreviewProvider { + static var previews: some View { + AppState.shared.token = "1" + return Group { + PushDeerWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent())) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + PushDeerWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent())) + .previewContext(WidgetPreviewContext(family: .systemMedium)) + PushDeerWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent())) + .previewContext(WidgetPreviewContext(family: .systemLarge)) + if #available(iOSApplicationExtension 15.0, *) { + PushDeerWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent())) + .previewContext(WidgetPreviewContext(family: .systemExtraLarge)) + } + } + } +} + +let placeholderList = [ + MessageItem(id: 1, uid: "", text: "展示最新通知", desp: "", type: "text", pushkey_name: "", created_at: ""), + MessageItem(id: 2, uid: "", text: "方便快速查看", desp: "", type: "text", pushkey_name: "", created_at: ""), + MessageItem(id: 3, uid: "", text: "保持消息同步", desp: "", type: "text", pushkey_name: "", created_at: ""), + MessageItem(id: 4, uid: "", text: "自动即时刷新", desp: "", type: "text", pushkey_name: "", created_at: ""), + MessageItem(id: -1, uid: "", text: "+其它\(8)条", desp: "", type: "text", pushkey_name: "", created_at: ""), +] diff --git a/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidgetExtension-SelfHosted.entitlements b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidgetExtension-SelfHosted.entitlements new file mode 100644 index 0000000..51fcac8 --- /dev/null +++ b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidgetExtension-SelfHosted.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.application-groups + + group.com.pushdeer.self.ios + + com.apple.security.network.client + + + diff --git a/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidgetExtension.entitlements b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidgetExtension.entitlements new file mode 100644 index 0000000..c8bd608 --- /dev/null +++ b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidgetExtension.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.application-groups + + group.com.pushdeer.app.ios + + com.apple.security.network.client + + +