Merge pull request #856 from mpretty-cyro/feature/updated-push-server
Updated Push Notification API Integration
This commit is contained in:
commit
32527d7e83
|
@ -19,7 +19,6 @@
|
|||
3496956021A2FC8100DCFE74 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3496955F21A2FC8100DCFE74 /* CloudKit.framework */; };
|
||||
34A6C28021E503E700B5B12E /* OWSImagePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34A6C27F21E503E600B5B12E /* OWSImagePickerController.swift */; };
|
||||
34A8B3512190A40E00218A25 /* MediaAlbumView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34A8B3502190A40E00218A25 /* MediaAlbumView.swift */; };
|
||||
34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B0796B1FCF46B000E248C2 /* MainAppContext.m */; };
|
||||
34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */; };
|
||||
34BECE2E1F7ABCE000D7438D /* GifPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34BECE2D1F7ABCE000D7438D /* GifPickerViewController.swift */; };
|
||||
34BECE301F7ABCF800D7438D /* GifPickerLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34BECE2F1F7ABCF800D7438D /* GifPickerLayout.swift */; };
|
||||
|
@ -313,27 +312,13 @@
|
|||
C33FDC29255A581F00E217F9 /* ReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */; };
|
||||
C33FDC45255A581F00E217F9 /* AppVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8B255A57FD00E217F9 /* AppVersion.m */; };
|
||||
C33FDC58255A582000E217F9 /* ReverseDispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA9E255A57FF00E217F9 /* ReverseDispatchQueue.swift */; };
|
||||
C33FDC78255A582000E217F9 /* TSConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDABE255A580100E217F9 /* TSConstants.m */; };
|
||||
C33FDC98255A582000E217F9 /* SwiftSingletons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDADE255A580400E217F9 /* SwiftSingletons.swift */; };
|
||||
C33FDC9A255A582000E217F9 /* ByteParser.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAE0255A580400E217F9 /* ByteParser.m */; };
|
||||
C33FDCD1255A582000E217F9 /* FunctionalUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB17255A580800E217F9 /* FunctionalUtil.m */; };
|
||||
C33FDCFA255A582000E217F9 /* SignalIOSProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB40255A580C00E217F9 /* SignalIOSProto.swift */; };
|
||||
C33FDD03255A582000E217F9 /* WeakTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB49255A580C00E217F9 /* WeakTimer.swift */; };
|
||||
C33FDD06255A582000E217F9 /* AppVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB4C255A580D00E217F9 /* AppVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C33FDD23255A582000E217F9 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB69255A580F00E217F9 /* FeatureFlags.swift */; };
|
||||
C33FDD32255A582000E217F9 /* OWSOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB78255A581000E217F9 /* OWSOperation.m */; };
|
||||
C33FDD3A255A582000E217F9 /* Notification+Loki.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB80255A581100E217F9 /* Notification+Loki.swift */; };
|
||||
C33FDD49255A582000E217F9 /* ParamParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB8F255A581200E217F9 /* ParamParser.swift */; };
|
||||
C33FDD5B255A582000E217F9 /* OWSOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBA1255A581400E217F9 /* OWSOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C33FDD6E255A582000E217F9 /* NSURLSessionDataTask+StatusCode.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBB4255A581600E217F9 /* NSURLSessionDataTask+StatusCode.m */; };
|
||||
C33FDD8D255A582000E217F9 /* OWSSignalAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD3255A581800E217F9 /* OWSSignalAddress.swift */; };
|
||||
C33FDD92255A582000E217F9 /* SignalIOS.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD8255A581900E217F9 /* SignalIOS.pb.swift */; };
|
||||
C33FDDB0255A582000E217F9 /* NSURLSessionDataTask+StatusCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF6255A581C00E217F9 /* NSURLSessionDataTask+StatusCode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C33FDDB3255A582000E217F9 /* OWSError.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF9255A581C00E217F9 /* OWSError.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C33FDDBD255A582000E217F9 /* ByteParser.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC03255A581D00E217F9 /* ByteParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C33FDDC5255A582000E217F9 /* OWSError.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC0B255A581D00E217F9 /* OWSError.m */; };
|
||||
C33FDDCC255A582000E217F9 /* TSConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC12255A581E00E217F9 /* TSConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C33FDDD0255A582000E217F9 /* FunctionalUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC16255A581E00E217F9 /* FunctionalUtil.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C3402FE52559036600EA6424 /* SessionUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C331FF1B2558F9D300070591 /* SessionUIKit.framework */; };
|
||||
C3471ECB2555356A00297E91 /* MessageSender+Encryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3471ECA2555356A00297E91 /* MessageSender+Encryption.swift */; };
|
||||
C3471F4C25553AB000297E91 /* MessageReceiver+Decryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3471F4B25553AB000297E91 /* MessageReceiver+Decryption.swift */; };
|
||||
|
@ -660,6 +645,7 @@
|
|||
FD6A7A692818BE7300035AC1 /* RetrieveDefaultOpenGroupRoomsJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6A7A682818BE7300035AC1 /* RetrieveDefaultOpenGroupRoomsJob.swift */; };
|
||||
FD6A7A6B2818C17C00035AC1 /* UpdateProfilePictureJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6A7A6A2818C17C00035AC1 /* UpdateProfilePictureJob.swift */; };
|
||||
FD6A7A6D2818C61500035AC1 /* _002_SetupStandardJobs.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6A7A6C2818C61500035AC1 /* _002_SetupStandardJobs.swift */; };
|
||||
FD6E4C8A2A1AEE4700C7C243 /* LegacyUnsubscribeRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6E4C892A1AEE4700C7C243 /* LegacyUnsubscribeRequest.swift */; };
|
||||
FD705A92278D051200F16121 /* ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A91278D051200F16121 /* ReusableView.swift */; };
|
||||
FD7115EB28C5D78E00B47552 /* ThreadSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7115EA28C5D78E00B47552 /* ThreadSettingsViewModel.swift */; };
|
||||
FD7115F228C6CB3900B47552 /* _010_AddThreadIdToFTS.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7115F128C6CB3900B47552 /* _010_AddThreadIdToFTS.swift */; };
|
||||
|
@ -769,6 +755,15 @@
|
|||
FDBB25E12983909300F1508E /* ConfigConvoInfoVolatileSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDBB25E02983909300F1508E /* ConfigConvoInfoVolatileSpec.swift */; };
|
||||
FDBB25E32988B13800F1508E /* _004_AddJobPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDBB25E22988B13800F1508E /* _004_AddJobPriority.swift */; };
|
||||
FDBB25E72988BBBE00F1508E /* UIContextualAction+Theming.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDBB25E62988BBBD00F1508E /* UIContextualAction+Theming.swift */; };
|
||||
FDC13D472A16E4CA007267C7 /* SubscribeRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC13D462A16E4CA007267C7 /* SubscribeRequest.swift */; };
|
||||
FDC13D492A16EC20007267C7 /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC13D482A16EC20007267C7 /* Service.swift */; };
|
||||
FDC13D4B2A16ECBA007267C7 /* SubscribeResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC13D4A2A16ECBA007267C7 /* SubscribeResponse.swift */; };
|
||||
FDC13D502A16EE50007267C7 /* PushNotificationAPIEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC13D4F2A16EE50007267C7 /* PushNotificationAPIEndpoint.swift */; };
|
||||
FDC13D522A16F22E007267C7 /* PushNotificationAPIRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC13D512A16F22E007267C7 /* PushNotificationAPIRequest.swift */; };
|
||||
FDC13D542A16FF29007267C7 /* LegacyGroupRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC13D532A16FF29007267C7 /* LegacyGroupRequest.swift */; };
|
||||
FDC13D562A171FE4007267C7 /* UnsubscribeRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC13D552A171FE4007267C7 /* UnsubscribeRequest.swift */; };
|
||||
FDC13D582A17207D007267C7 /* UnsubscribeResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC13D572A17207D007267C7 /* UnsubscribeResponse.swift */; };
|
||||
FDC13D5A2A1721C5007267C7 /* LegacyNotifyRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC13D592A1721C5007267C7 /* LegacyNotifyRequest.swift */; };
|
||||
FDC2908727D7047F005DAE71 /* RoomSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2908627D7047F005DAE71 /* RoomSpec.swift */; };
|
||||
FDC2908927D70656005DAE71 /* RoomPollInfoSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2908827D70656005DAE71 /* RoomPollInfoSpec.swift */; };
|
||||
FDC2908B27D707F3005DAE71 /* SendMessageRequestSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2908A27D707F3005DAE71 /* SendMessageRequestSpec.swift */; };
|
||||
|
@ -786,7 +781,7 @@
|
|||
FDC4380927B31D4E00C60D73 /* OpenGroupAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4380827B31D4E00C60D73 /* OpenGroupAPIError.swift */; };
|
||||
FDC4381727B32EC700C60D73 /* Personalization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381627B32EC700C60D73 /* Personalization.swift */; };
|
||||
FDC4382027B36ADC00C60D73 /* SOGSEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381F27B36ADC00C60D73 /* SOGSEndpoint.swift */; };
|
||||
FDC4382F27B383AF00C60D73 /* PushServerResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382E27B383AF00C60D73 /* PushServerResponse.swift */; };
|
||||
FDC4382F27B383AF00C60D73 /* LegacyPushServerResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382E27B383AF00C60D73 /* LegacyPushServerResponse.swift */; };
|
||||
FDC4383827B3863200C60D73 /* VersionResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4383727B3863200C60D73 /* VersionResponse.swift */; };
|
||||
FDC4385D27B4C18900C60D73 /* Room.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385C27B4C18900C60D73 /* Room.swift */; };
|
||||
FDC4385F27B4C4A200C60D73 /* PinnedMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385E27B4C4A200C60D73 /* PinnedMessage.swift */; };
|
||||
|
@ -809,11 +804,13 @@
|
|||
FDC438CD27BC641200C60D73 /* Set+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438CC27BC641200C60D73 /* Set+Utilities.swift */; };
|
||||
FDC6D6F32860607300B04575 /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7542807C4BB004C14C5 /* Environment.swift */; };
|
||||
FDC6D7602862B3F600B04575 /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC6D75F2862B3F600B04575 /* Dependencies.swift */; };
|
||||
FDCD2E032A41294E00964D6A /* LegacyGroupOnlyRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCD2E022A41294E00964D6A /* LegacyGroupOnlyRequest.swift */; };
|
||||
FDCDB8DE2810F73B00352A0C /* Differentiable+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCDB8DD2810F73B00352A0C /* Differentiable+Utilities.swift */; };
|
||||
FDCDB8E02811007F00352A0C /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCDB8DF2811007F00352A0C /* HomeViewModel.swift */; };
|
||||
FDD2506E283711D600198BDA /* DifferenceKit+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD2506D283711D600198BDA /* DifferenceKit+Utilities.swift */; };
|
||||
FDD250702837199200198BDA /* GarbageCollectionJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD2506F2837199200198BDA /* GarbageCollectionJob.swift */; };
|
||||
FDD250722837234B00198BDA /* MediaGalleryNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD250712837234B00198BDA /* MediaGalleryNavigationController.swift */; };
|
||||
FDD82C3F2A205D0A00425F05 /* ProcessResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD82C3E2A205D0A00425F05 /* ProcessResult.swift */; };
|
||||
FDDC08F229A300E800BF9681 /* LibSessionTypeConversionUtilitiesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDDC08F129A300E800BF9681 /* LibSessionTypeConversionUtilitiesSpec.swift */; };
|
||||
FDDCBDA829E776BF00303C38 /* seed2-2023-2y.crt in Resources */ = {isa = PBXBuildFile; fileRef = FDDCBDA229E776BF00303C38 /* seed2-2023-2y.crt */; };
|
||||
FDDCBDA929E776BF00303C38 /* seed1-2023-2y.crt in Resources */ = {isa = PBXBuildFile; fileRef = FDDCBDA329E776BF00303C38 /* seed1-2023-2y.crt */; };
|
||||
|
@ -823,6 +820,7 @@
|
|||
FDDCBDAD29E776BF00303C38 /* seed3-2023-2y.der in Resources */ = {isa = PBXBuildFile; fileRef = FDDCBDA729E776BF00303C38 /* seed3-2023-2y.der */; };
|
||||
FDDF074429C3E3D000E5E8B5 /* FetchRequest+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDDF074329C3E3D000E5E8B5 /* FetchRequest+Utilities.swift */; };
|
||||
FDDF074A29DAB36900E5E8B5 /* JobRunnerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDDF074929DAB36900E5E8B5 /* JobRunnerSpec.swift */; };
|
||||
FDE125232A837E4E002DA685 /* MainAppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE125222A837E4E002DA685 /* MainAppContext.swift */; };
|
||||
FDE658A129418C7900A33BC1 /* CryptoKit+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE658A029418C7900A33BC1 /* CryptoKit+Utilities.swift */; };
|
||||
FDE658A329418E2F00A33BC1 /* KeyPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE658A229418E2F00A33BC1 /* KeyPair.swift */; };
|
||||
FDE6E99829F8E63A00F93C5D /* Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE6E99729F8E63A00F93C5D /* Accessibility.swift */; };
|
||||
|
@ -907,6 +905,9 @@
|
|||
FDF848F329413DB0007DCAE5 /* ImagePickerHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF848F229413DB0007DCAE5 /* ImagePickerHandler.swift */; };
|
||||
FDF848F529413EEC007DCAE5 /* SessionCell+Styling.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF848F429413EEC007DCAE5 /* SessionCell+Styling.swift */; };
|
||||
FDF848F729414477007DCAE5 /* CurrentUserPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF848F629414477007DCAE5 /* CurrentUserPoller.swift */; };
|
||||
FDFBB74B2A1EFF4900CA7350 /* Bencode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFBB74A2A1EFF4900CA7350 /* Bencode.swift */; };
|
||||
FDFBB74D2A1F3C4E00CA7350 /* NotificationMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFBB74C2A1F3C4E00CA7350 /* NotificationMetadata.swift */; };
|
||||
FDFBB7542A2023EB00CA7350 /* BencodeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFBB7532A2023EB00CA7350 /* BencodeSpec.swift */; };
|
||||
FDFC4D9A29F0C51500992FB6 /* String+Trimming.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D22553860900C340D1 /* String+Trimming.swift */; };
|
||||
FDFD645927F26C6800808CA1 /* Array+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D12553860800C340D1 /* Array+Utilities.swift */; };
|
||||
FDFD645D27F273F300808CA1 /* MockGeneralCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */; };
|
||||
|
@ -1112,8 +1113,6 @@
|
|||
3496955F21A2FC8100DCFE74 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
|
||||
34A6C27F21E503E600B5B12E /* OWSImagePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSImagePickerController.swift; sourceTree = "<group>"; };
|
||||
34A8B3502190A40E00218A25 /* MediaAlbumView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaAlbumView.swift; sourceTree = "<group>"; };
|
||||
34B0796B1FCF46B000E248C2 /* MainAppContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainAppContext.m; sourceTree = "<group>"; };
|
||||
34B0796C1FCF46B000E248C2 /* MainAppContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainAppContext.h; sourceTree = "<group>"; };
|
||||
34B0796E1FD07B1E00E248C2 /* SignalShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SignalShareExtension.entitlements; sourceTree = "<group>"; };
|
||||
34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicatorView.swift; sourceTree = "<group>"; };
|
||||
34BECE2D1F7ABCE000D7438D /* GifPickerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GifPickerViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -1415,9 +1414,7 @@
|
|||
C33FDA8E255A57FD00E217F9 /* OWSFileSystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSFileSystem.m; sourceTree = "<group>"; };
|
||||
C33FDA9E255A57FF00E217F9 /* ReverseDispatchQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReverseDispatchQueue.swift; sourceTree = "<group>"; };
|
||||
C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BuildConfiguration.swift; sourceTree = "<group>"; };
|
||||
C33FDABE255A580100E217F9 /* TSConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSConstants.m; sourceTree = "<group>"; };
|
||||
C33FDADE255A580400E217F9 /* SwiftSingletons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftSingletons.swift; sourceTree = "<group>"; };
|
||||
C33FDAE0255A580400E217F9 /* ByteParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ByteParser.m; sourceTree = "<group>"; };
|
||||
C33FDAEF255A580500E217F9 /* NSData+Image.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+Image.m"; sourceTree = "<group>"; };
|
||||
C33FDAF1255A580500E217F9 /* ThumbnailService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThumbnailService.swift; sourceTree = "<group>"; };
|
||||
C33FDAF2255A580500E217F9 /* ProxiedContentDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProxiedContentDownloader.swift; sourceTree = "<group>"; };
|
||||
|
@ -1425,7 +1422,6 @@
|
|||
C33FDAFD255A580600E217F9 /* LRUCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LRUCache.swift; sourceTree = "<group>"; };
|
||||
C33FDB01255A580700E217F9 /* AppReadiness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppReadiness.h; sourceTree = "<group>"; };
|
||||
C33FDB14255A580800E217F9 /* OWSMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMath.h; sourceTree = "<group>"; };
|
||||
C33FDB17255A580800E217F9 /* FunctionalUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FunctionalUtil.m; sourceTree = "<group>"; };
|
||||
C33FDB1C255A580900E217F9 /* UIImage+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+OWS.h"; sourceTree = "<group>"; };
|
||||
C33FDB22255A580900E217F9 /* OWSMediaUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSMediaUtils.swift; sourceTree = "<group>"; };
|
||||
C33FDB29255A580A00E217F9 /* NSData+Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+Image.h"; sourceTree = "<group>"; };
|
||||
|
@ -1433,7 +1429,6 @@
|
|||
C33FDB38255A580B00E217F9 /* OWSBackgroundTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackgroundTask.h; sourceTree = "<group>"; };
|
||||
C33FDB3A255A580B00E217F9 /* Poller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Poller.swift; sourceTree = "<group>"; };
|
||||
C33FDB3F255A580C00E217F9 /* String+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+SSK.swift"; sourceTree = "<group>"; };
|
||||
C33FDB40255A580C00E217F9 /* SignalIOSProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalIOSProto.swift; sourceTree = "<group>"; };
|
||||
C33FDB41255A580C00E217F9 /* MIMETypeUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIMETypeUtil.m; sourceTree = "<group>"; };
|
||||
C33FDB49255A580C00E217F9 /* WeakTimer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakTimer.swift; sourceTree = "<group>"; };
|
||||
C33FDB4C255A580D00E217F9 /* AppVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppVersion.h; sourceTree = "<group>"; };
|
||||
|
@ -1444,27 +1439,17 @@
|
|||
C33FDB6B255A580F00E217F9 /* SNUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SNUserDefaults.swift; sourceTree = "<group>"; };
|
||||
C33FDB75255A581000E217F9 /* AppReadiness.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppReadiness.m; sourceTree = "<group>"; };
|
||||
C33FDB77255A581000E217F9 /* NSUserDefaults+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+OWS.m"; sourceTree = "<group>"; };
|
||||
C33FDB78255A581000E217F9 /* OWSOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOperation.m; sourceTree = "<group>"; };
|
||||
C33FDB80255A581100E217F9 /* Notification+Loki.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Notification+Loki.swift"; sourceTree = "<group>"; };
|
||||
C33FDB81255A581100E217F9 /* UIImage+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+OWS.m"; sourceTree = "<group>"; };
|
||||
C33FDB85255A581100E217F9 /* AppContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppContext.m; sourceTree = "<group>"; };
|
||||
C33FDB8A255A581200E217F9 /* AppContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppContext.h; sourceTree = "<group>"; };
|
||||
C33FDB8F255A581200E217F9 /* ParamParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParamParser.swift; sourceTree = "<group>"; };
|
||||
C33FDBA1255A581400E217F9 /* OWSOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOperation.h; sourceTree = "<group>"; };
|
||||
C33FDBA8255A581500E217F9 /* LinkPreviewDraft.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkPreviewDraft.swift; sourceTree = "<group>"; };
|
||||
C33FDBAB255A581500E217F9 /* OWSFileSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSFileSystem.h; sourceTree = "<group>"; };
|
||||
C33FDBB4255A581600E217F9 /* NSURLSessionDataTask+StatusCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLSessionDataTask+StatusCode.m"; sourceTree = "<group>"; };
|
||||
C33FDBB6255A581600E217F9 /* DataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DataSource.m; sourceTree = "<group>"; };
|
||||
C33FDBBC255A581600E217F9 /* SSKKeychainStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSKKeychainStorage.swift; sourceTree = "<group>"; };
|
||||
C33FDBD3255A581800E217F9 /* OWSSignalAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSSignalAddress.swift; sourceTree = "<group>"; };
|
||||
C33FDBD8255A581900E217F9 /* SignalIOS.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalIOS.pb.swift; sourceTree = "<group>"; };
|
||||
C33FDBDE255A581900E217F9 /* PushNotificationAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushNotificationAPI.swift; sourceTree = "<group>"; };
|
||||
C33FDBF6255A581C00E217F9 /* NSURLSessionDataTask+StatusCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLSessionDataTask+StatusCode.h"; sourceTree = "<group>"; };
|
||||
C33FDBF9255A581C00E217F9 /* OWSError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSError.h; sourceTree = "<group>"; };
|
||||
C33FDC03255A581D00E217F9 /* ByteParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ByteParser.h; sourceTree = "<group>"; };
|
||||
C33FDC0B255A581D00E217F9 /* OWSError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSError.m; sourceTree = "<group>"; };
|
||||
C33FDC12255A581E00E217F9 /* TSConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSConstants.h; sourceTree = "<group>"; };
|
||||
C33FDC16255A581E00E217F9 /* FunctionalUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FunctionalUtil.h; sourceTree = "<group>"; };
|
||||
C33FDC1B255A581F00E217F9 /* OWSBackgroundTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackgroundTask.m; sourceTree = "<group>"; };
|
||||
C3471ECA2555356A00297E91 /* MessageSender+Encryption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageSender+Encryption.swift"; sourceTree = "<group>"; };
|
||||
C3471F4B25553AB000297E91 /* MessageReceiver+Decryption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageReceiver+Decryption.swift"; sourceTree = "<group>"; };
|
||||
|
@ -1778,6 +1763,7 @@
|
|||
FD6A7A682818BE7300035AC1 /* RetrieveDefaultOpenGroupRoomsJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetrieveDefaultOpenGroupRoomsJob.swift; sourceTree = "<group>"; };
|
||||
FD6A7A6A2818C17C00035AC1 /* UpdateProfilePictureJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateProfilePictureJob.swift; sourceTree = "<group>"; };
|
||||
FD6A7A6C2818C61500035AC1 /* _002_SetupStandardJobs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _002_SetupStandardJobs.swift; sourceTree = "<group>"; };
|
||||
FD6E4C892A1AEE4700C7C243 /* LegacyUnsubscribeRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyUnsubscribeRequest.swift; sourceTree = "<group>"; };
|
||||
FD705A91278D051200F16121 /* ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = "<group>"; };
|
||||
FD7115EA28C5D78E00B47552 /* ThreadSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadSettingsViewModel.swift; sourceTree = "<group>"; };
|
||||
FD7115EF28C5D7DE00B47552 /* SessionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionHeaderView.swift; sourceTree = "<group>"; };
|
||||
|
@ -1877,6 +1863,15 @@
|
|||
FDBB25E02983909300F1508E /* ConfigConvoInfoVolatileSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigConvoInfoVolatileSpec.swift; sourceTree = "<group>"; };
|
||||
FDBB25E22988B13800F1508E /* _004_AddJobPriority.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _004_AddJobPriority.swift; sourceTree = "<group>"; };
|
||||
FDBB25E62988BBBD00F1508E /* UIContextualAction+Theming.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIContextualAction+Theming.swift"; sourceTree = "<group>"; };
|
||||
FDC13D462A16E4CA007267C7 /* SubscribeRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscribeRequest.swift; sourceTree = "<group>"; };
|
||||
FDC13D482A16EC20007267C7 /* Service.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Service.swift; sourceTree = "<group>"; };
|
||||
FDC13D4A2A16ECBA007267C7 /* SubscribeResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscribeResponse.swift; sourceTree = "<group>"; };
|
||||
FDC13D4F2A16EE50007267C7 /* PushNotificationAPIEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationAPIEndpoint.swift; sourceTree = "<group>"; };
|
||||
FDC13D512A16F22E007267C7 /* PushNotificationAPIRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationAPIRequest.swift; sourceTree = "<group>"; };
|
||||
FDC13D532A16FF29007267C7 /* LegacyGroupRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyGroupRequest.swift; sourceTree = "<group>"; };
|
||||
FDC13D552A171FE4007267C7 /* UnsubscribeRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsubscribeRequest.swift; sourceTree = "<group>"; };
|
||||
FDC13D572A17207D007267C7 /* UnsubscribeResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsubscribeResponse.swift; sourceTree = "<group>"; };
|
||||
FDC13D592A1721C5007267C7 /* LegacyNotifyRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyNotifyRequest.swift; sourceTree = "<group>"; };
|
||||
FDC2908627D7047F005DAE71 /* RoomSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSpec.swift; sourceTree = "<group>"; };
|
||||
FDC2908827D70656005DAE71 /* RoomPollInfoSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollInfoSpec.swift; sourceTree = "<group>"; };
|
||||
FDC2908A27D707F3005DAE71 /* SendMessageRequestSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMessageRequestSpec.swift; sourceTree = "<group>"; };
|
||||
|
@ -1893,7 +1888,7 @@
|
|||
FDC4380827B31D4E00C60D73 /* OpenGroupAPIError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupAPIError.swift; sourceTree = "<group>"; };
|
||||
FDC4381627B32EC700C60D73 /* Personalization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Personalization.swift; sourceTree = "<group>"; };
|
||||
FDC4381F27B36ADC00C60D73 /* SOGSEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOGSEndpoint.swift; sourceTree = "<group>"; };
|
||||
FDC4382E27B383AF00C60D73 /* PushServerResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushServerResponse.swift; sourceTree = "<group>"; };
|
||||
FDC4382E27B383AF00C60D73 /* LegacyPushServerResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyPushServerResponse.swift; sourceTree = "<group>"; };
|
||||
FDC4383727B3863200C60D73 /* VersionResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionResponse.swift; sourceTree = "<group>"; };
|
||||
FDC4383D27B4708600C60D73 /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
|
||||
FDC4385C27B4C18900C60D73 /* Room.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Room.swift; sourceTree = "<group>"; };
|
||||
|
@ -1917,11 +1912,13 @@
|
|||
FDC438CA27BB7DB100C60D73 /* UpdateMessageRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateMessageRequest.swift; sourceTree = "<group>"; };
|
||||
FDC438CC27BC641200C60D73 /* Set+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Set+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FDC6D75F2862B3F600B04575 /* Dependencies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dependencies.swift; sourceTree = "<group>"; };
|
||||
FDCD2E022A41294E00964D6A /* LegacyGroupOnlyRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyGroupOnlyRequest.swift; sourceTree = "<group>"; };
|
||||
FDCDB8DD2810F73B00352A0C /* Differentiable+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Differentiable+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FDCDB8DF2811007F00352A0C /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; };
|
||||
FDD2506D283711D600198BDA /* DifferenceKit+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DifferenceKit+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FDD2506F2837199200198BDA /* GarbageCollectionJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GarbageCollectionJob.swift; sourceTree = "<group>"; };
|
||||
FDD250712837234B00198BDA /* MediaGalleryNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaGalleryNavigationController.swift; sourceTree = "<group>"; };
|
||||
FDD82C3E2A205D0A00425F05 /* ProcessResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessResult.swift; sourceTree = "<group>"; };
|
||||
FDDC08F129A300E800BF9681 /* LibSessionTypeConversionUtilitiesSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibSessionTypeConversionUtilitiesSpec.swift; sourceTree = "<group>"; };
|
||||
FDDCBDA229E776BF00303C38 /* seed2-2023-2y.crt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "seed2-2023-2y.crt"; sourceTree = "<group>"; };
|
||||
FDDCBDA329E776BF00303C38 /* seed1-2023-2y.crt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "seed1-2023-2y.crt"; sourceTree = "<group>"; };
|
||||
|
@ -1931,6 +1928,7 @@
|
|||
FDDCBDA729E776BF00303C38 /* seed3-2023-2y.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "seed3-2023-2y.der"; sourceTree = "<group>"; };
|
||||
FDDF074329C3E3D000E5E8B5 /* FetchRequest+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FetchRequest+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FDDF074929DAB36900E5E8B5 /* JobRunnerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JobRunnerSpec.swift; sourceTree = "<group>"; };
|
||||
FDE125222A837E4E002DA685 /* MainAppContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainAppContext.swift; sourceTree = "<group>"; };
|
||||
FDE658A029418C7900A33BC1 /* CryptoKit+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CryptoKit+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FDE658A229418E2F00A33BC1 /* KeyPair.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyPair.swift; sourceTree = "<group>"; };
|
||||
FDE6E99729F8E63A00F93C5D /* Accessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Accessibility.swift; sourceTree = "<group>"; };
|
||||
|
@ -2018,6 +2016,9 @@
|
|||
FDF848F229413DB0007DCAE5 /* ImagePickerHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImagePickerHandler.swift; sourceTree = "<group>"; };
|
||||
FDF848F429413EEC007DCAE5 /* SessionCell+Styling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SessionCell+Styling.swift"; sourceTree = "<group>"; };
|
||||
FDF848F629414477007DCAE5 /* CurrentUserPoller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrentUserPoller.swift; sourceTree = "<group>"; };
|
||||
FDFBB74A2A1EFF4900CA7350 /* Bencode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bencode.swift; sourceTree = "<group>"; };
|
||||
FDFBB74C2A1F3C4E00CA7350 /* NotificationMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationMetadata.swift; sourceTree = "<group>"; };
|
||||
FDFBB7532A2023EB00CA7350 /* BencodeSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BencodeSpec.swift; sourceTree = "<group>"; };
|
||||
FDFD645A27F26D4600808CA1 /* Data+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockGeneralCache.swift; sourceTree = "<group>"; };
|
||||
FDFDE123282D04F20098B17F /* MediaDismissAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaDismissAnimationController.swift; sourceTree = "<group>"; };
|
||||
|
@ -3121,6 +3122,7 @@
|
|||
C379DC6825672B5E0002D4EB /* Notifications */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FDC13D4E2A16EE41007267C7 /* Types */,
|
||||
FDC4382D27B383A600C60D73 /* Models */,
|
||||
FDF0B7502807BA56004C14C5 /* NotificationsProtocol.swift */,
|
||||
C33FDBDE255A581900E217F9 /* PushNotificationAPI.swift */,
|
||||
|
@ -3363,34 +3365,20 @@
|
|||
FD71161D28D9772700B47552 /* UIViewController+OWS.swift */,
|
||||
C38EF2B1255B6D9C007E1867 /* UIViewController+Utilities.swift */,
|
||||
C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */,
|
||||
C33FDBF9255A581C00E217F9 /* OWSError.h */,
|
||||
C33FDC0B255A581D00E217F9 /* OWSError.m */,
|
||||
C33FDBA1255A581400E217F9 /* OWSOperation.h */,
|
||||
C33FDB78255A581000E217F9 /* OWSOperation.m */,
|
||||
C33FDBD3255A581800E217F9 /* OWSSignalAddress.swift */,
|
||||
C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */,
|
||||
C33FDBD8255A581900E217F9 /* SignalIOS.pb.swift */,
|
||||
C33FDB40255A580C00E217F9 /* SignalIOSProto.swift */,
|
||||
C33FDC12255A581E00E217F9 /* TSConstants.h */,
|
||||
C33FDABE255A580100E217F9 /* TSConstants.m */,
|
||||
C33FDB4C255A580D00E217F9 /* AppVersion.h */,
|
||||
C33FDA8B255A57FD00E217F9 /* AppVersion.m */,
|
||||
C38EF3E4255B6DF4007E1867 /* CommonStrings.swift */,
|
||||
C38EF304255B6DBE007E1867 /* ImageCache.swift */,
|
||||
C38EF2F2255B6DBC007E1867 /* Searcher.swift */,
|
||||
C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */,
|
||||
C33FDA8B255A57FD00E217F9 /* AppVersion.m */,
|
||||
C33FDB69255A580F00E217F9 /* FeatureFlags.swift */,
|
||||
C33FDB80255A581100E217F9 /* Notification+Loki.swift */,
|
||||
C33FDC16255A581E00E217F9 /* FunctionalUtil.h */,
|
||||
C33FDB17255A580800E217F9 /* FunctionalUtil.m */,
|
||||
C33FDB8F255A581200E217F9 /* ParamParser.swift */,
|
||||
C33FDADE255A580400E217F9 /* SwiftSingletons.swift */,
|
||||
C33FDB49255A580C00E217F9 /* WeakTimer.swift */,
|
||||
C33FDA9E255A57FF00E217F9 /* ReverseDispatchQueue.swift */,
|
||||
C33FDBF6255A581C00E217F9 /* NSURLSessionDataTask+StatusCode.h */,
|
||||
C33FDBB4255A581600E217F9 /* NSURLSessionDataTask+StatusCode.m */,
|
||||
C33FDC03255A581D00E217F9 /* ByteParser.h */,
|
||||
C33FDAE0255A580400E217F9 /* ByteParser.m */,
|
||||
C38EF3DD255B6DF1007E1867 /* UIAlertController+OWS.swift */,
|
||||
C38EF241255B6D67007E1867 /* Collection+OWS.swift */,
|
||||
C38EF3AE255B6DE5007E1867 /* OrderedDictionary.swift */,
|
||||
|
@ -3420,8 +3408,7 @@
|
|||
34330A581E7875FB00DF2FB9 /* Fonts */,
|
||||
B66DBF4919D5BBC8006EA940 /* Images.xcassets */,
|
||||
45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */,
|
||||
34B0796C1FCF46B000E248C2 /* MainAppContext.h */,
|
||||
34B0796B1FCF46B000E248C2 /* MainAppContext.m */,
|
||||
FDE125222A837E4E002DA685 /* MainAppContext.swift */,
|
||||
C3CA3AA0255CDA7000F4C6D4 /* Mnemonic */,
|
||||
B67EBF5C19194AC60084CCFD /* Settings.bundle */,
|
||||
B657DDC91911A40500F45B0C /* Signal.entitlements */,
|
||||
|
@ -3548,6 +3535,7 @@
|
|||
FD09796527F6B0A800936362 /* Utilities */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FDFBB74A2A1EFF4900CA7350 /* Bencode.swift */,
|
||||
FD97B23F2A3FEB050027DD57 /* ARC4RandomNumberGenerator.swift */,
|
||||
FDA8EB0F280F8238002B68E5 /* Codable+Utilities.swift */,
|
||||
FD3003692A3ADD6000B5A5FB /* CExceptionHelper.h */,
|
||||
|
@ -4013,6 +4001,7 @@
|
|||
FD83B9B927CF20A5005E1583 /* General */,
|
||||
FDDF074829DAB35200E5E8B5 /* JobRunner */,
|
||||
FD9B30F1293EA0AF008DEE3E /* Networking */,
|
||||
FDFBB7522A2023DE00CA7350 /* Utilities */,
|
||||
FD29598E2A43BE5400888A17 /* Utilities */,
|
||||
);
|
||||
path = SessionUtilitiesKitTests;
|
||||
|
@ -4151,6 +4140,16 @@
|
|||
path = Configs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FDC13D4E2A16EE41007267C7 /* Types */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FDC13D482A16EC20007267C7 /* Service.swift */,
|
||||
FDD82C3E2A205D0A00425F05 /* ProcessResult.swift */,
|
||||
FDC13D4F2A16EE50007267C7 /* PushNotificationAPIEndpoint.swift */,
|
||||
);
|
||||
path = Types;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FDC2909227D710A9005DAE71 /* Types */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -4201,7 +4200,17 @@
|
|||
FDC4382D27B383A600C60D73 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FDC4382E27B383AF00C60D73 /* PushServerResponse.swift */,
|
||||
FDC13D512A16F22E007267C7 /* PushNotificationAPIRequest.swift */,
|
||||
FDC13D462A16E4CA007267C7 /* SubscribeRequest.swift */,
|
||||
FDC13D4A2A16ECBA007267C7 /* SubscribeResponse.swift */,
|
||||
FDC13D552A171FE4007267C7 /* UnsubscribeRequest.swift */,
|
||||
FDC13D572A17207D007267C7 /* UnsubscribeResponse.swift */,
|
||||
FD6E4C892A1AEE4700C7C243 /* LegacyUnsubscribeRequest.swift */,
|
||||
FDC13D532A16FF29007267C7 /* LegacyGroupRequest.swift */,
|
||||
FDCD2E022A41294E00964D6A /* LegacyGroupOnlyRequest.swift */,
|
||||
FDC4382E27B383AF00C60D73 /* LegacyPushServerResponse.swift */,
|
||||
FDC13D592A1721C5007267C7 /* LegacyNotifyRequest.swift */,
|
||||
FDFBB74C2A1F3C4E00CA7350 /* NotificationMetadata.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
@ -4401,6 +4410,14 @@
|
|||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FDFBB7522A2023DE00CA7350 /* Utilities */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FDFBB7532A2023EB00CA7350 /* BencodeSpec.swift */,
|
||||
);
|
||||
path = Utilities;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FDFDE122282D04E30098B17F /* Transitions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -4427,12 +4444,6 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C33FDDB0255A582000E217F9 /* NSURLSessionDataTask+StatusCode.h in Headers */,
|
||||
C33FDDD0255A582000E217F9 /* FunctionalUtil.h in Headers */,
|
||||
C33FDD5B255A582000E217F9 /* OWSOperation.h in Headers */,
|
||||
C33FDDCC255A582000E217F9 /* TSConstants.h in Headers */,
|
||||
C33FDDBD255A582000E217F9 /* ByteParser.h in Headers */,
|
||||
C33FDDB3255A582000E217F9 /* OWSError.h in Headers */,
|
||||
C38EF35E255B6DCC007E1867 /* OWSViewController.h in Headers */,
|
||||
C33FD9AF255A548A00E217F9 /* SignalUtilitiesKit.h in Headers */,
|
||||
C33FDD06255A582000E217F9 /* AppVersion.h in Headers */,
|
||||
|
@ -4733,6 +4744,7 @@
|
|||
D221A080169C9E5E00537ABF /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
DefaultBuildSystemTypeForWorkspace = Original;
|
||||
LastSwiftUpdateCheck = 1430;
|
||||
LastTestingUpgradeCheck = 0600;
|
||||
|
@ -5494,10 +5506,8 @@
|
|||
C38EF30C255B6DBF007E1867 /* ScreenLock.swift in Sources */,
|
||||
C38EF363255B6DCC007E1867 /* ModalActivityIndicatorViewController.swift in Sources */,
|
||||
C38EF38A255B6DD2007E1867 /* AttachmentCaptionToolbar.swift in Sources */,
|
||||
C33FDCD1255A582000E217F9 /* FunctionalUtil.m in Sources */,
|
||||
C38EF402255B6DF7007E1867 /* CommonStrings.swift in Sources */,
|
||||
C38EF3C1255B6DE7007E1867 /* ImageEditorBrushViewController.swift in Sources */,
|
||||
C33FDD32255A582000E217F9 /* OWSOperation.m in Sources */,
|
||||
C3F0A530255C80BC007BE2A3 /* NoopNotificationsManager.swift in Sources */,
|
||||
C33FDD8D255A582000E217F9 /* OWSSignalAddress.swift in Sources */,
|
||||
C38EF388255B6DD2007E1867 /* AttachmentApprovalViewController.swift in Sources */,
|
||||
|
@ -5505,7 +5515,6 @@
|
|||
C33FDC29255A581F00E217F9 /* ReachabilityManager.swift in Sources */,
|
||||
C38EF407255B6DF7007E1867 /* Toast.swift in Sources */,
|
||||
C38EF38C255B6DD2007E1867 /* ApprovalRailCellView.swift in Sources */,
|
||||
C33FDD92255A582000E217F9 /* SignalIOS.pb.swift in Sources */,
|
||||
C33FDC45255A581F00E217F9 /* AppVersion.m in Sources */,
|
||||
C38EF3C7255B6DE7007E1867 /* ImageEditorCanvasView.swift in Sources */,
|
||||
C38EF400255B6DF7007E1867 /* GalleryRailView.swift in Sources */,
|
||||
|
@ -5514,7 +5523,6 @@
|
|||
C38EF3BA255B6DE7007E1867 /* ImageEditorItem.swift in Sources */,
|
||||
C33FDD3A255A582000E217F9 /* Notification+Loki.swift in Sources */,
|
||||
C38EF24E255B6D67007E1867 /* Collection+OWS.swift in Sources */,
|
||||
C33FDCFA255A582000E217F9 /* SignalIOSProto.swift in Sources */,
|
||||
C38EF24D255B6D67007E1867 /* UIView+OWS.swift in Sources */,
|
||||
C38EF38B255B6DD2007E1867 /* AttachmentPrepViewController.swift in Sources */,
|
||||
C38EF405255B6DF7007E1867 /* OWSButton.swift in Sources */,
|
||||
|
@ -5530,9 +5538,7 @@
|
|||
FD52090B28B59BB4006098F6 /* ScreenLockViewController.swift in Sources */,
|
||||
C38EF401255B6DF7007E1867 /* VideoPlayerView.swift in Sources */,
|
||||
C38EF3BD255B6DE7007E1867 /* ImageEditorTransform.swift in Sources */,
|
||||
C33FDC9A255A582000E217F9 /* ByteParser.m in Sources */,
|
||||
C33FDC58255A582000E217F9 /* ReverseDispatchQueue.swift in Sources */,
|
||||
C33FDC78255A582000E217F9 /* TSConstants.m in Sources */,
|
||||
C38EF324255B6DBF007E1867 /* Bench.swift in Sources */,
|
||||
FDCDB8DE2810F73B00352A0C /* Differentiable+Utilities.swift in Sources */,
|
||||
C38EF3F9255B6DF7007E1867 /* OWSLayerView.swift in Sources */,
|
||||
|
@ -5544,12 +5550,10 @@
|
|||
C38EF386255B6DD2007E1867 /* AttachmentApprovalInputAccessoryView.swift in Sources */,
|
||||
B8C2B2C82563685C00551B4D /* CircleView.swift in Sources */,
|
||||
C38EF331255B6DBF007E1867 /* UIGestureRecognizer+OWS.swift in Sources */,
|
||||
C33FDDC5255A582000E217F9 /* OWSError.m in Sources */,
|
||||
FD848B9C284435D7000E298B /* AppSetup.swift in Sources */,
|
||||
C38EF31C255B6DBF007E1867 /* Searcher.swift in Sources */,
|
||||
C38EF2B3255B6D9C007E1867 /* UIViewController+Utilities.swift in Sources */,
|
||||
C38EF3BE255B6DE7007E1867 /* OrderedDictionary.swift in Sources */,
|
||||
C33FDD6E255A582000E217F9 /* NSURLSessionDataTask+StatusCode.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -5652,6 +5656,7 @@
|
|||
C3D9E4C02567767F0040E4F3 /* DataSource.m in Sources */,
|
||||
C32C5DD2256DD9E5003C73A2 /* LRUCache.swift in Sources */,
|
||||
FD71160228C8255900B47552 /* UIControl+Combine.swift in Sources */,
|
||||
FDFBB74B2A1EFF4900CA7350 /* Bencode.swift in Sources */,
|
||||
FD9004152818B46300ABAAF6 /* JobRunner.swift in Sources */,
|
||||
FDF8487929405906007DCAE5 /* HTTPQueryParam.swift in Sources */,
|
||||
FD17D7CA27F546D900122BE0 /* _001_InitialSetupMigration.swift in Sources */,
|
||||
|
@ -5751,6 +5756,7 @@
|
|||
FD245C672850665E00B966DD /* AttachmentDownloadJob.swift in Sources */,
|
||||
C300A5D32554B05A00555489 /* TypingIndicator.swift in Sources */,
|
||||
7B521E0A29BFF84400C3C36A /* GroupLeavingJob.swift in Sources */,
|
||||
FDC13D582A17207D007267C7 /* UnsubscribeResponse.swift in Sources */,
|
||||
FD09799927FFC1A300936362 /* Attachment.swift in Sources */,
|
||||
FD245C5F2850662200B966DD /* OWSWindowManager.m in Sources */,
|
||||
C3471ECB2555356A00297E91 /* MessageSender+Encryption.swift in Sources */,
|
||||
|
@ -5770,6 +5776,7 @@
|
|||
FD245C5A2850660100B966DD /* LinkPreviewDraft.swift in Sources */,
|
||||
FDD250702837199200198BDA /* GarbageCollectionJob.swift in Sources */,
|
||||
FD6A7A6B2818C17C00035AC1 /* UpdateProfilePictureJob.swift in Sources */,
|
||||
FDD82C3F2A205D0A00425F05 /* ProcessResult.swift in Sources */,
|
||||
FD716E6A2850327900C96BF4 /* EndCallMode.swift in Sources */,
|
||||
FDF0B75C2807F41D004C14C5 /* MessageSender+Convenience.swift in Sources */,
|
||||
7B81682A28B6F1420069F315 /* ReactionResponse.swift in Sources */,
|
||||
|
@ -5783,6 +5790,7 @@
|
|||
FD245C57285065F100B966DD /* Poller.swift in Sources */,
|
||||
FDA8EAFE280E8B78002B68E5 /* FailedMessageSendsJob.swift in Sources */,
|
||||
FD245C6A2850666F00B966DD /* FileServerAPI.swift in Sources */,
|
||||
FDFBB74D2A1F3C4E00CA7350 /* NotificationMetadata.swift in Sources */,
|
||||
FDC4386927B4E6B800C60D73 /* String+Utlities.swift in Sources */,
|
||||
FD716E6628502EE200C96BF4 /* CurrentCallProtocol.swift in Sources */,
|
||||
FD8ECF9029381FC200C0D1BB /* SessionUtil+UserProfile.swift in Sources */,
|
||||
|
@ -5797,6 +5805,7 @@
|
|||
FD37EA0F28AB3330003AE748 /* _006_FixHiddenModAdminSupport.swift in Sources */,
|
||||
FD2B4AFD294688D000AB4848 /* SessionUtil+Contacts.swift in Sources */,
|
||||
7B81682328A4C1210069F315 /* UpdateTypes.swift in Sources */,
|
||||
FDC13D472A16E4CA007267C7 /* SubscribeRequest.swift in Sources */,
|
||||
FD2B4AFF2946C93200AB4848 /* ConfigurationSyncJob.swift in Sources */,
|
||||
FDC438A627BB113A00C60D73 /* UserUnbanRequest.swift in Sources */,
|
||||
FD5C72FB284F0EA10029977D /* MessageReceiver+DataExtractionNotification.swift in Sources */,
|
||||
|
@ -5805,18 +5814,22 @@
|
|||
C379DCF4256735770002D4EB /* VisibleMessage+Attachment.swift in Sources */,
|
||||
FDB4BBC72838B91E00B7C95D /* LinkPreviewError.swift in Sources */,
|
||||
FD09798327FD1A1500936362 /* ClosedGroup.swift in Sources */,
|
||||
FDC13D542A16FF29007267C7 /* LegacyGroupRequest.swift in Sources */,
|
||||
B8B320B7258C30D70020074B /* HTMLMetadata.swift in Sources */,
|
||||
FD8ECF8B2935DB4B00C0D1BB /* SharedConfigMessage.swift in Sources */,
|
||||
FD09798727FD1B7800936362 /* GroupMember.swift in Sources */,
|
||||
FDB4BBC92839BEF000B7C95D /* ProfileManagerError.swift in Sources */,
|
||||
FDCD2E032A41294E00964D6A /* LegacyGroupOnlyRequest.swift in Sources */,
|
||||
FD3E0C84283B5835002A425C /* SessionThreadViewModel.swift in Sources */,
|
||||
FD09C5EC282B8F18000CE219 /* AttachmentError.swift in Sources */,
|
||||
FD17D79927F40AB800122BE0 /* _003_YDBToGRDBMigration.swift in Sources */,
|
||||
FDF0B7512807BA56004C14C5 /* NotificationsProtocol.swift in Sources */,
|
||||
B8DE1FB426C22F2F0079C9CE /* WebRTCSession.swift in Sources */,
|
||||
FDC13D5A2A1721C5007267C7 /* LegacyNotifyRequest.swift in Sources */,
|
||||
FDC6D6F32860607300B04575 /* Environment.swift in Sources */,
|
||||
C3A3A171256E1D25004D228D /* SSKReachabilityManager.swift in Sources */,
|
||||
FD245C59285065FC00B966DD /* ControlMessage.swift in Sources */,
|
||||
FD6E4C8A2A1AEE4700C7C243 /* LegacyUnsubscribeRequest.swift in Sources */,
|
||||
B8DE1FB626C22FCB0079C9CE /* CallMessage.swift in Sources */,
|
||||
FD245C50285065C700B966DD /* VisibleMessage+Quote.swift in Sources */,
|
||||
FD8ECF892935AB7200C0D1BB /* SessionUtilError.swift in Sources */,
|
||||
|
@ -5842,16 +5855,18 @@
|
|||
FD6A7A692818BE7300035AC1 /* RetrieveDefaultOpenGroupRoomsJob.swift in Sources */,
|
||||
FD09798D27FD1D8900936362 /* DisappearingMessageConfiguration.swift in Sources */,
|
||||
FDF0B75A2807F3A3004C14C5 /* MessageSenderError.swift in Sources */,
|
||||
FDC4382F27B383AF00C60D73 /* PushServerResponse.swift in Sources */,
|
||||
FDC4382F27B383AF00C60D73 /* LegacyPushServerResponse.swift in Sources */,
|
||||
FDC4386327B4D94E00C60D73 /* SOGSMessage.swift in Sources */,
|
||||
FD245C692850666800B966DD /* ExpirationTimerUpdate.swift in Sources */,
|
||||
FD42F9A8285064B800A0C77D /* PushNotificationAPI.swift in Sources */,
|
||||
FD245C6C2850669200B966DD /* MessageReceiveJob.swift in Sources */,
|
||||
FD43EE9D297A5190009C87C5 /* SessionUtil+UserGroups.swift in Sources */,
|
||||
FD83B9CC27D179BC005E1583 /* FSEndpoint.swift in Sources */,
|
||||
FDC13D4B2A16ECBA007267C7 /* SubscribeResponse.swift in Sources */,
|
||||
FD7115F228C6CB3900B47552 /* _010_AddThreadIdToFTS.swift in Sources */,
|
||||
FD716E6428502DDD00C96BF4 /* CallManagerProtocol.swift in Sources */,
|
||||
FDC438C727BB6DF000C60D73 /* DirectMessage.swift in Sources */,
|
||||
FDC13D502A16EE50007267C7 /* PushNotificationAPIEndpoint.swift in Sources */,
|
||||
FD432434299C6985008A0213 /* PendingReadReceipt.swift in Sources */,
|
||||
FDC4381727B32EC700C60D73 /* Personalization.swift in Sources */,
|
||||
FD245C51285065CC00B966DD /* MessageReceiver.swift in Sources */,
|
||||
|
@ -5877,7 +5892,9 @@
|
|||
FD09796E27FA6D0000936362 /* Contact.swift in Sources */,
|
||||
C38D5E8D2575011E00B6A65C /* MessageSender+ClosedGroups.swift in Sources */,
|
||||
FD5C72F7284F0E560029977D /* MessageReceiver+ReadReceipts.swift in Sources */,
|
||||
FDC13D492A16EC20007267C7 /* Service.swift in Sources */,
|
||||
FD778B6429B189FF001BAC6B /* _014_GenerateInitialUserConfigDumps.swift in Sources */,
|
||||
FDC13D562A171FE4007267C7 /* UnsubscribeRequest.swift in Sources */,
|
||||
C32C598A256D0664003C73A2 /* SNProtoEnvelope+Conversion.swift in Sources */,
|
||||
FDC438CB27BB7DB100C60D73 /* UpdateMessageRequest.swift in Sources */,
|
||||
FD8ECF7F2934298100C0D1BB /* ConfigDump.swift in Sources */,
|
||||
|
@ -5901,6 +5918,7 @@
|
|||
FD245C682850666300B966DD /* Message+Destination.swift in Sources */,
|
||||
FDF8488029405994007DCAE5 /* HTTPQueryParam+OpenGroup.swift in Sources */,
|
||||
FD09798527FD1A6500936362 /* ClosedGroupKeyPair.swift in Sources */,
|
||||
FDC13D522A16F22E007267C7 /* PushNotificationAPIRequest.swift in Sources */,
|
||||
FD245C632850664600B966DD /* Configuration.swift in Sources */,
|
||||
C32C5DBF256DD743003C73A2 /* ClosedGroupPoller.swift in Sources */,
|
||||
C352A35B2557824E00338F3E /* AttachmentUploadJob.swift in Sources */,
|
||||
|
@ -5975,10 +5993,10 @@
|
|||
B83F2B88240CB75A000A54AB /* UIImage+Scaling.swift in Sources */,
|
||||
3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */,
|
||||
4C090A1B210FD9C7001FD7F9 /* HapticFeedback.swift in Sources */,
|
||||
FDE125232A837E4E002DA685 /* MainAppContext.swift in Sources */,
|
||||
7B9F71D12852EEE2006DFE7B /* EmojiWithSkinTones+String.swift in Sources */,
|
||||
34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */,
|
||||
B886B4A92398BA1500211ABE /* QRCode.swift in Sources */,
|
||||
34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */,
|
||||
34A8B3512190A40E00218A25 /* MediaAlbumView.swift in Sources */,
|
||||
FD09C5E828264937000CE219 /* MediaDetailViewController.swift in Sources */,
|
||||
3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */,
|
||||
|
@ -6164,6 +6182,7 @@
|
|||
FD2AAAF228ED57B500A49611 /* SynchronousStorage.swift in Sources */,
|
||||
FD078E4927E02576000769AF /* CommonMockedExtensions.swift in Sources */,
|
||||
FD83B9BF27CF2294005E1583 /* TestConstants.swift in Sources */,
|
||||
FDFBB7542A2023EB00CA7350 /* BencodeSpec.swift in Sources */,
|
||||
FD9DD2732A72516D00ECB68E /* TestExtensions.swift in Sources */,
|
||||
FD83B9BB27CF20AF005E1583 /* SessionIdSpec.swift in Sources */,
|
||||
FDC290A927D9B46D005DAE71 /* NimbleExtensions.swift in Sources */,
|
||||
|
@ -6406,7 +6425,7 @@
|
|||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 421;
|
||||
CURRENT_PROJECT_VERSION = 422;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||
|
@ -6430,7 +6449,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.3.2;
|
||||
MARKETING_VERSION = 2.4.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -6478,7 +6497,7 @@
|
|||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 421;
|
||||
CURRENT_PROJECT_VERSION = 422;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
|
@ -6507,7 +6526,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.3.2;
|
||||
MARKETING_VERSION = 2.4.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -6543,7 +6562,7 @@
|
|||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 421;
|
||||
CURRENT_PROJECT_VERSION = 422;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||
|
@ -6566,7 +6585,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.3.2;
|
||||
MARKETING_VERSION = 2.4.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension";
|
||||
|
@ -6617,7 +6636,7 @@
|
|||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 421;
|
||||
CURRENT_PROJECT_VERSION = 422;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
|
@ -6645,7 +6664,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.3.2;
|
||||
MARKETING_VERSION = 2.4.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension";
|
||||
|
@ -7577,7 +7596,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 421;
|
||||
CURRENT_PROJECT_VERSION = 422;
|
||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -7615,7 +7634,7 @@
|
|||
"$(SRCROOT)",
|
||||
);
|
||||
LLVM_LTO = NO;
|
||||
MARKETING_VERSION = 2.3.2;
|
||||
MARKETING_VERSION = 2.4.0;
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger";
|
||||
|
@ -7648,7 +7667,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 421;
|
||||
CURRENT_PROJECT_VERSION = 422;
|
||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -7686,7 +7705,7 @@
|
|||
"$(SRCROOT)",
|
||||
);
|
||||
LLVM_LTO = NO;
|
||||
MARKETING_VERSION = 2.3.2;
|
||||
MARKETING_VERSION = 2.4.0;
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger";
|
||||
PRODUCT_NAME = Session;
|
||||
|
|
|
@ -9,6 +9,8 @@ import WebRTC
|
|||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
import SessionSnodeKit
|
||||
|
||||
public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
|
||||
@objc static let isEnabled = true
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import UIKit
|
||||
import GRDB
|
||||
import SessionUtilitiesKit
|
||||
|
||||
extension SessionCallManager {
|
||||
@discardableResult
|
||||
|
|
|
@ -6,6 +6,7 @@ import GRDB
|
|||
import SessionMessagingKit
|
||||
import SignalCoreKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public final class SessionCallManager: NSObject, CallManagerProtocol {
|
||||
let provider: CXProvider?
|
||||
|
|
|
@ -4,6 +4,7 @@ import UIKit
|
|||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class IncomingCallBanner: UIView, UIGestureRecognizerDelegate {
|
||||
private static let swipeToOperateThreshold: CGFloat = 60
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import UIKit
|
||||
import WebRTC
|
||||
import SessionUIKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class MiniCallView: UIView, RTCVideoViewDelegate {
|
||||
var callVC: CallVC
|
||||
|
|
|
@ -7,6 +7,7 @@ import DifferenceKit
|
|||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class EditClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegate {
|
||||
private struct GroupMemberDisplayInfo: FetchableRecord, Equatable, Hashable, Decodable, Differentiable {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import UIKit
|
||||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
extension ContextMenuVC {
|
||||
struct Action {
|
||||
|
|
|
@ -5,6 +5,7 @@ import GRDB
|
|||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionUIKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public class StyledSearchController: UISearchController {
|
||||
public override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
|
|
|
@ -11,6 +11,7 @@ import SessionUIKit
|
|||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionSnodeKit
|
||||
|
||||
extension ConversationVC:
|
||||
InputViewDelegate,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class InputViewButton: UIView {
|
||||
private let icon: UIImage?
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import UIKit
|
||||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class CallMessageCell: MessageCell {
|
||||
private static let iconSize: CGFloat = 16
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import UIKit
|
||||
import SessionMessagingKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public class MediaAlbumView: UIStackView {
|
||||
private let items: [Attachment]
|
||||
|
|
|
@ -6,6 +6,7 @@ import SessionUIKit
|
|||
import SessionMessagingKit
|
||||
import SignalCoreKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public class MediaView: UIView {
|
||||
static let contentMode: UIView.ContentMode = .scaleAspectFill
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import UIKit
|
||||
import SessionUIKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
@objc class TypingIndicatorView: UIStackView {
|
||||
// This represents the spacing between the dots
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import UIKit
|
||||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public enum SwipeState {
|
||||
case began
|
||||
|
|
|
@ -9,6 +9,7 @@ import SessionUIKit
|
|||
import SessionMessagingKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
import SessionSnodeKit
|
||||
|
||||
class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.NavButton, ThreadSettingsViewModel.Section, ThreadSettingsViewModel.Setting> {
|
||||
// MARK: - Config
|
||||
|
|
|
@ -5,6 +5,7 @@ import DifferenceKit
|
|||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class ReactionListSheet: BaseVC {
|
||||
public struct ReactionSummary: Hashable, Differentiable {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Foundation
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
extension Emoji {
|
||||
private static let availableCache: Atomic<[Emoji:Bool]> = Atomic([:])
|
||||
|
|
|
@ -6,6 +6,7 @@ import DifferenceKit
|
|||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
class MessageRequestsViewController: BaseVC, SessionUtilRespondingViewController, UITableViewDelegate, UITableViewDataSource {
|
||||
private static let loadingHeaderHeight: CGFloat = 40
|
||||
|
|
|
@ -4,6 +4,7 @@ import Foundation
|
|||
import GRDB
|
||||
import DifferenceKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public class MessageRequestsViewModel {
|
||||
public typealias SectionModel = ArraySection<Section, SessionThreadViewModel>
|
||||
|
|
|
@ -7,6 +7,7 @@ import SessionUIKit
|
|||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionSnodeKit
|
||||
|
||||
final class NewDMVC: BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, QRScannerDelegate {
|
||||
private var shouldShowBackButton: Bool = true
|
||||
|
|
|
@ -5,6 +5,7 @@ import MediaPlayer
|
|||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
// This kind of view is tricky. I've tried to organize things in the
|
||||
// simplest possible way.
|
||||
|
@ -359,54 +360,54 @@ import SignalCoreKit
|
|||
|
||||
@objc func handlePinch(sender: UIPinchGestureRecognizer) {
|
||||
switch sender.state {
|
||||
case .possible:
|
||||
break
|
||||
case .began:
|
||||
srcTranslationAtPinchStart = srcTranslation
|
||||
imageScaleAtPinchStart = imageScale
|
||||
case .possible: break
|
||||
case .began:
|
||||
srcTranslationAtPinchStart = srcTranslation
|
||||
imageScaleAtPinchStart = imageScale
|
||||
|
||||
lastPinchLocation =
|
||||
sender.location(in: sender.view)
|
||||
lastPinchScale = sender.scale
|
||||
break
|
||||
case .changed, .ended:
|
||||
if sender.numberOfTouches > 1 {
|
||||
let location =
|
||||
lastPinchLocation =
|
||||
sender.location(in: sender.view)
|
||||
let scaleDiff = sender.scale / lastPinchScale
|
||||
|
||||
// Update scaling.
|
||||
let srcCropSizeBeforeScalePoints = CGSize(width: srcDefaultCropSizePoints.width / imageScale,
|
||||
height: srcDefaultCropSizePoints.height / imageScale)
|
||||
imageScale = max(kMinImageScale, min(kMaxImageScale, imageScale * scaleDiff))
|
||||
let srcCropSizeAfterScalePoints = CGSize(width: srcDefaultCropSizePoints.width / imageScale,
|
||||
height: srcDefaultCropSizePoints.height / imageScale)
|
||||
// Since the translation state reflects the "upper left" corner of the crop region, we need to
|
||||
// adjust the translation when scaling to preserve the "center" of the crop region.
|
||||
srcTranslation.x += (srcCropSizeBeforeScalePoints.width - srcCropSizeAfterScalePoints.width) * 0.5
|
||||
srcTranslation.y += (srcCropSizeBeforeScalePoints.height - srcCropSizeAfterScalePoints.height) * 0.5
|
||||
|
||||
// Update translation.
|
||||
let viewSizePoints = imageView.frame.size
|
||||
let srcCropSizePoints = CGSize(width: srcDefaultCropSizePoints.width / imageScale,
|
||||
height: srcDefaultCropSizePoints.height / imageScale)
|
||||
|
||||
let viewToSrcRatio = srcCropSizePoints.width / viewSizePoints.width
|
||||
|
||||
let gestureTranslation = CGPoint(x: location.x - lastPinchLocation.x,
|
||||
y: location.y - lastPinchLocation.y)
|
||||
|
||||
srcTranslation = CGPoint(x: srcTranslation.x + gestureTranslation.x * -viewToSrcRatio,
|
||||
y: srcTranslation.y + gestureTranslation.y * -viewToSrcRatio)
|
||||
|
||||
lastPinchLocation = location
|
||||
lastPinchScale = sender.scale
|
||||
}
|
||||
break
|
||||
case .cancelled, .failed:
|
||||
srcTranslation = srcTranslationAtPinchStart
|
||||
imageScale = imageScaleAtPinchStart
|
||||
break
|
||||
|
||||
case .changed, .ended:
|
||||
if sender.numberOfTouches > 1 {
|
||||
let location =
|
||||
sender.location(in: sender.view)
|
||||
let scaleDiff = sender.scale / lastPinchScale
|
||||
|
||||
// Update scaling.
|
||||
let srcCropSizeBeforeScalePoints = CGSize(width: srcDefaultCropSizePoints.width / imageScale,
|
||||
height: srcDefaultCropSizePoints.height / imageScale)
|
||||
imageScale = max(kMinImageScale, min(kMaxImageScale, imageScale * scaleDiff))
|
||||
let srcCropSizeAfterScalePoints = CGSize(width: srcDefaultCropSizePoints.width / imageScale,
|
||||
height: srcDefaultCropSizePoints.height / imageScale)
|
||||
// Since the translation state reflects the "upper left" corner of the crop region, we need to
|
||||
// adjust the translation when scaling to preserve the "center" of the crop region.
|
||||
srcTranslation.x += (srcCropSizeBeforeScalePoints.width - srcCropSizeAfterScalePoints.width) * 0.5
|
||||
srcTranslation.y += (srcCropSizeBeforeScalePoints.height - srcCropSizeAfterScalePoints.height) * 0.5
|
||||
|
||||
// Update translation.
|
||||
let viewSizePoints = imageView.frame.size
|
||||
let srcCropSizePoints = CGSize(width: srcDefaultCropSizePoints.width / imageScale,
|
||||
height: srcDefaultCropSizePoints.height / imageScale)
|
||||
|
||||
let viewToSrcRatio = srcCropSizePoints.width / viewSizePoints.width
|
||||
|
||||
let gestureTranslation = CGPoint(x: location.x - lastPinchLocation.x,
|
||||
y: location.y - lastPinchLocation.y)
|
||||
|
||||
srcTranslation = CGPoint(x: srcTranslation.x + gestureTranslation.x * -viewToSrcRatio,
|
||||
y: srcTranslation.y + gestureTranslation.y * -viewToSrcRatio)
|
||||
|
||||
lastPinchLocation = location
|
||||
lastPinchScale = sender.scale
|
||||
}
|
||||
|
||||
case .cancelled, .failed:
|
||||
srcTranslation = srcTranslationAtPinchStart
|
||||
imageScale = imageScaleAtPinchStart
|
||||
|
||||
@unknown default: break
|
||||
}
|
||||
|
||||
updateImageLayout()
|
||||
|
@ -416,29 +417,28 @@ import SignalCoreKit
|
|||
|
||||
@objc func handlePan(sender: UIPanGestureRecognizer) {
|
||||
switch sender.state {
|
||||
case .possible:
|
||||
break
|
||||
case .began:
|
||||
srcTranslationAtPanStart = srcTranslation
|
||||
break
|
||||
case .changed, .ended:
|
||||
let viewSizePoints = imageView.frame.size
|
||||
let srcCropSizePoints = CGSize(width: srcDefaultCropSizePoints.width / imageScale,
|
||||
height: srcDefaultCropSizePoints.height / imageScale)
|
||||
case .possible: break
|
||||
case .began:
|
||||
srcTranslationAtPanStart = srcTranslation
|
||||
|
||||
case .changed, .ended:
|
||||
let viewSizePoints = imageView.frame.size
|
||||
let srcCropSizePoints = CGSize(width: srcDefaultCropSizePoints.width / imageScale,
|
||||
height: srcDefaultCropSizePoints.height / imageScale)
|
||||
|
||||
let viewToSrcRatio = srcCropSizePoints.width / viewSizePoints.width
|
||||
let viewToSrcRatio = srcCropSizePoints.width / viewSizePoints.width
|
||||
|
||||
let gestureTranslation =
|
||||
sender.translation(in: sender.view)
|
||||
let gestureTranslation =
|
||||
sender.translation(in: sender.view)
|
||||
|
||||
// Update translation.
|
||||
srcTranslation = CGPoint(x: srcTranslationAtPanStart.x + gestureTranslation.x * -viewToSrcRatio,
|
||||
y: srcTranslationAtPanStart.y + gestureTranslation.y * -viewToSrcRatio)
|
||||
break
|
||||
case .cancelled, .failed:
|
||||
srcTranslation
|
||||
= srcTranslationAtPanStart
|
||||
break
|
||||
// Update translation.
|
||||
srcTranslation = CGPoint(x: srcTranslationAtPanStart.x + gestureTranslation.x * -viewToSrcRatio,
|
||||
y: srcTranslationAtPanStart.y + gestureTranslation.y * -viewToSrcRatio)
|
||||
|
||||
case .cancelled, .failed:
|
||||
srcTranslation = srcTranslationAtPanStart
|
||||
|
||||
@unknown default: break
|
||||
}
|
||||
|
||||
updateImageLayout()
|
||||
|
|
|
@ -7,6 +7,7 @@ import DifferenceKit
|
|||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public class DocumentTileViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import Combine
|
|||
import YYImage
|
||||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
class GifPickerCell: UICollectionViewCell {
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import Reachability
|
|||
import SignalUtilitiesKit
|
||||
import SessionUIKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollectionViewDataSource, UICollectionViewDelegate, GifPickerLayoutDelegate {
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public class GiphyDownloader: ProxiedContentDownloader {
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import Photos
|
|||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
protocol ImagePickerGridControllerDelegate: AnyObject {
|
||||
func imagePickerDidCompleteSelection(_ imagePicker: ImagePickerGridController)
|
||||
|
@ -155,6 +156,8 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
|
|||
case .cancelled, .ended, .failed:
|
||||
collectionView.isUserInteractionEnabled = true
|
||||
collectionView.isScrollEnabled = true
|
||||
|
||||
@unknown default: break
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import SessionUIKit
|
|||
import SignalUtilitiesKit
|
||||
import SessionMessagingKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public enum MediaGalleryOption {
|
||||
case sliderEnabled
|
||||
|
|
|
@ -6,6 +6,8 @@ import SessionUIKit
|
|||
import SessionMessagingKit
|
||||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
import SessionSnodeKit
|
||||
|
||||
class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, MediaDetailViewControllerDelegate, InteractivelyDismissableViewController {
|
||||
class DynamicallySizedView: UIView {
|
||||
|
|
|
@ -7,6 +7,7 @@ import DifferenceKit
|
|||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public class MediaTileViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import AVFoundation
|
|||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
protocol PhotoCaptureViewControllerDelegate: AnyObject {
|
||||
func photoCaptureViewController(_ photoCaptureViewController: PhotoCaptureViewController, didFinishProcessingAttachment attachment: SignalAttachment)
|
||||
|
|
|
@ -6,6 +6,7 @@ import Photos
|
|||
import CoreServices
|
||||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
protocol PhotoLibraryDelegate: AnyObject {
|
||||
func photoLibraryDidChange(_ photoLibrary: PhotoLibrary)
|
||||
|
|
|
@ -6,6 +6,7 @@ import Photos
|
|||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionUIKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
class SendMediaNavigationController: UINavigationController {
|
||||
public override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
|
@ -539,8 +540,8 @@ private struct MediaLibrarySelection: Hashable, Equatable {
|
|||
let asset: PHAsset
|
||||
let signalAttachmentPublisher: AnyPublisher<SignalAttachment, Error>
|
||||
|
||||
var hashValue: Int {
|
||||
return asset.hashValue
|
||||
func hash(into hasher: inout Hasher) {
|
||||
asset.hash(into: &hasher)
|
||||
}
|
||||
|
||||
var publisher: AnyPublisher<MediaLibraryAttachment, Error> {
|
||||
|
@ -559,8 +560,8 @@ private struct MediaLibraryAttachment: Hashable, Equatable {
|
|||
let asset: PHAsset
|
||||
let signalAttachment: SignalAttachment
|
||||
|
||||
public var hashValue: Int {
|
||||
return asset.hashValue
|
||||
func hash(into hasher: inout Hasher) {
|
||||
asset.hash(into: &hasher)
|
||||
}
|
||||
|
||||
public static func == (lhs: MediaLibraryAttachment, rhs: MediaLibraryAttachment) -> Bool {
|
||||
|
|
|
@ -9,6 +9,7 @@ import SessionMessagingKit
|
|||
import SessionUtilitiesKit
|
||||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionSnodeKit
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
|
||||
|
|
|
@ -4,6 +4,7 @@ import Foundation
|
|||
import SessionUtilitiesKit
|
||||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionMessagingKit
|
||||
|
||||
public class AppEnvironment {
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <SessionUtilitiesKit/AppContext.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString *const ReportedApplicationStateDidChangeNotification;
|
||||
|
||||
@interface MainAppContext : NSObject <AppContext>
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,314 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MainAppContext.h"
|
||||
#import "Session-Swift.h"
|
||||
#import <SignalCoreKit/OWSAsserts.h>
|
||||
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const ReportedApplicationStateDidChangeNotification = @"ReportedApplicationStateDidChangeNotification";
|
||||
|
||||
@interface MainAppContext ()
|
||||
|
||||
@property (atomic) UIApplicationState reportedApplicationState;
|
||||
|
||||
@property (nonatomic, nullable) NSMutableArray<AppActiveBlock> *appActiveBlocks;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation MainAppContext
|
||||
|
||||
@synthesize mainWindow = _mainWindow;
|
||||
@synthesize appLaunchTime = _appLaunchTime;
|
||||
@synthesize wasWokenUpByPushNotification = _wasWokenUpByPushNotification;
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
self.reportedApplicationState = UIApplicationStateInactive;
|
||||
|
||||
_appLaunchTime = [NSDate new];
|
||||
_wasWokenUpByPushNotification = false;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(applicationWillEnterForeground:)
|
||||
name:UIApplicationWillEnterForegroundNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(applicationDidEnterBackground:)
|
||||
name:UIApplicationDidEnterBackgroundNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(applicationWillResignActive:)
|
||||
name:UIApplicationWillResignActiveNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(applicationDidBecomeActive:)
|
||||
name:UIApplicationDidBecomeActiveNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(applicationWillTerminate:)
|
||||
name:UIApplicationWillTerminateNotification
|
||||
object:nil];
|
||||
|
||||
self.appActiveBlocks = [NSMutableArray new];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
#pragma mark - Notifications
|
||||
|
||||
- (void)setReportedApplicationState:(UIApplicationState)reportedApplicationState
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
if (_reportedApplicationState == reportedApplicationState) {
|
||||
return;
|
||||
}
|
||||
_reportedApplicationState = reportedApplicationState;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:ReportedApplicationStateDidChangeNotification
|
||||
object:nil
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground:(NSNotification *)notification
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
self.reportedApplicationState = UIApplicationStateInactive;
|
||||
|
||||
OWSLogInfo(@"");
|
||||
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:OWSApplicationWillEnterForegroundNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(NSNotification *)notification
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
self.reportedApplicationState = UIApplicationStateBackground;
|
||||
|
||||
OWSLogInfo(@"");
|
||||
[DDLog flushLog];
|
||||
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:OWSApplicationDidEnterBackgroundNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
self.reportedApplicationState = UIApplicationStateInactive;
|
||||
|
||||
OWSLogInfo(@"");
|
||||
[DDLog flushLog];
|
||||
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:OWSApplicationWillResignActiveNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification *)notification
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
self.reportedApplicationState = UIApplicationStateActive;
|
||||
|
||||
OWSLogInfo(@"");
|
||||
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:OWSApplicationDidBecomeActiveNotification object:nil];
|
||||
|
||||
[self runAppActiveBlocks];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)notification
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
OWSLogInfo(@"");
|
||||
[DDLog flushLog];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (BOOL)isMainApp
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)isMainAppAndActive
|
||||
{
|
||||
return [UIApplication sharedApplication].applicationState == UIApplicationStateActive;
|
||||
}
|
||||
|
||||
- (BOOL)isShareExtension {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isRTL
|
||||
{
|
||||
// FIXME: We should try to remove this as we've had to add a hack to ensure the first call to this runs on the main thread
|
||||
static BOOL isRTL = NO;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
isRTL = [[UIApplication sharedApplication] userInterfaceLayoutDirection]
|
||||
== UIUserInterfaceLayoutDirectionRightToLeft;
|
||||
});
|
||||
return isRTL;
|
||||
}
|
||||
|
||||
- (void)setStatusBarHidden:(BOOL)isHidden animated:(BOOL)isAnimated
|
||||
{
|
||||
[[UIApplication sharedApplication] setStatusBarHidden:isHidden animated:isAnimated];
|
||||
}
|
||||
|
||||
- (CGFloat)statusBarHeight
|
||||
{
|
||||
return [UIApplication sharedApplication].statusBarFrame.size.height;
|
||||
}
|
||||
|
||||
- (BOOL)isInBackground
|
||||
{
|
||||
return self.reportedApplicationState == UIApplicationStateBackground;
|
||||
}
|
||||
|
||||
- (BOOL)isAppForegroundAndActive
|
||||
{
|
||||
return self.reportedApplicationState == UIApplicationStateActive;
|
||||
}
|
||||
|
||||
- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:
|
||||
(BackgroundTaskExpirationHandler)expirationHandler
|
||||
{
|
||||
return [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:expirationHandler];
|
||||
}
|
||||
|
||||
- (void)endBackgroundTask:(UIBackgroundTaskIdentifier)backgroundTaskIdentifier
|
||||
{
|
||||
[UIApplication.sharedApplication endBackgroundTask:backgroundTaskIdentifier];
|
||||
}
|
||||
|
||||
- (void)ensureSleepBlocking:(BOOL)shouldBeBlocking blockingObjects:(NSArray<id> *)blockingObjects
|
||||
{
|
||||
if (UIApplication.sharedApplication.isIdleTimerDisabled != shouldBeBlocking) {
|
||||
if (shouldBeBlocking) {
|
||||
NSMutableString *logString =
|
||||
[NSMutableString stringWithFormat:@"Blocking sleep because of: %@", blockingObjects.firstObject];
|
||||
if (blockingObjects.count > 1) {
|
||||
[logString appendString:[NSString stringWithFormat:@"(and %lu others)", blockingObjects.count - 1]];
|
||||
}
|
||||
OWSLogInfo(@"%@", logString);
|
||||
} else {
|
||||
OWSLogInfo(@"Unblocking Sleep.");
|
||||
}
|
||||
}
|
||||
UIApplication.sharedApplication.idleTimerDisabled = shouldBeBlocking;
|
||||
}
|
||||
|
||||
- (void)setMainAppBadgeNumber:(NSInteger)value
|
||||
{
|
||||
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:value];
|
||||
[[NSUserDefaults sharedLokiProject] setInteger:value forKey:@"currentBadgeNumber"];
|
||||
[[NSUserDefaults sharedLokiProject] synchronize];
|
||||
}
|
||||
|
||||
- (nullable UIViewController *)frontmostViewController
|
||||
{
|
||||
return UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts;
|
||||
}
|
||||
|
||||
- (nullable UIAlertAction *)openSystemSettingsAction
|
||||
{
|
||||
return [UIAlertAction actionWithTitle:CommonStrings.openSettingsButton
|
||||
accessibilityIdentifier:[NSString stringWithFormat:@"%@.%@", self.class, @"system_settings"]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
[UIApplication.sharedApplication openSystemSettings];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setNetworkActivityIndicatorVisible:(BOOL)value
|
||||
{
|
||||
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:value];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)runNowOrWhenMainAppIsActive:(AppActiveBlock)block
|
||||
{
|
||||
OWSAssertDebug(block);
|
||||
|
||||
[Threading dispatchMainThreadSafe:^{
|
||||
if (self.isMainAppAndActive) {
|
||||
// App active blocks typically will be used to safely access the
|
||||
// shared data container, so use a background task to protect this
|
||||
// work.
|
||||
OWSBackgroundTask *_Nullable backgroundTask =
|
||||
[OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
|
||||
block();
|
||||
OWSAssertDebug(backgroundTask);
|
||||
backgroundTask = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
[self.appActiveBlocks addObject:block];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)runAppActiveBlocks
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
OWSAssertDebug(self.isMainAppAndActive);
|
||||
|
||||
// App active blocks typically will be used to safely access the
|
||||
// shared data container, so use a background task to protect this
|
||||
// work.
|
||||
OWSBackgroundTask *_Nullable backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
|
||||
|
||||
NSArray<AppActiveBlock> *appActiveBlocks = [self.appActiveBlocks copy];
|
||||
[self.appActiveBlocks removeAllObjects];
|
||||
for (AppActiveBlock block in appActiveBlocks) {
|
||||
block();
|
||||
}
|
||||
|
||||
OWSAssertDebug(backgroundTask);
|
||||
backgroundTask = nil;
|
||||
}
|
||||
|
||||
- (NSString *)appDocumentDirectoryPath
|
||||
{
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *documentDirectoryURL =
|
||||
[[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
|
||||
return [documentDirectoryURL path];
|
||||
}
|
||||
|
||||
- (NSString *)appSharedDataDirectoryPath
|
||||
{
|
||||
NSURL *groupContainerDirectoryURL =
|
||||
[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:SignalApplicationGroup];
|
||||
return [groupContainerDirectoryURL path];
|
||||
}
|
||||
|
||||
- (NSUserDefaults *)appUserDefaults
|
||||
{
|
||||
return [[NSUserDefaults alloc] initWithSuiteName:SignalApplicationGroup];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,253 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class MainAppContext: NSObject, AppContext {
|
||||
var reportedApplicationState: UIApplication.State
|
||||
|
||||
let appLaunchTime = Date()
|
||||
let isMainApp: Bool = true
|
||||
var isMainAppAndActive: Bool { UIApplication.shared.applicationState == .active }
|
||||
var isShareExtension: Bool = false
|
||||
var appActiveBlocks: [AppActiveBlock] = []
|
||||
|
||||
var mainWindow: UIWindow?
|
||||
var wasWokenUpByPushNotification: Bool = false
|
||||
|
||||
private static var _isRTL: Bool = {
|
||||
return (UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft)
|
||||
}()
|
||||
|
||||
var isRTL: Bool { return MainAppContext._isRTL }
|
||||
|
||||
var statusBarHeight: CGFloat { UIApplication.shared.statusBarFrame.size.height }
|
||||
var openSystemSettingsAction: UIAlertAction? {
|
||||
let result = UIAlertAction(
|
||||
title: "OPEN_SETTINGS_BUTTON".localized(),
|
||||
style: .default
|
||||
) { _ in UIApplication.shared.openSystemSettings() }
|
||||
result.accessibilityIdentifier = "\(type(of: self)).system_settings"
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
override init() {
|
||||
self.reportedApplicationState = .inactive
|
||||
|
||||
super.init()
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(applicationWillEnterForeground(notification:)),
|
||||
name: UIApplication.willEnterForegroundNotification,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(applicationDidEnterBackground(notification:)),
|
||||
name: UIApplication.didEnterBackgroundNotification,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(applicationWillResignActive(notification:)),
|
||||
name: UIApplication.willResignActiveNotification,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(applicationDidBecomeActive(notification:)),
|
||||
name: UIApplication.didBecomeActiveNotification,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(applicationWillTerminate(notification:)),
|
||||
name: UIApplication.willTerminateNotification,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
// MARK: - Notifications
|
||||
|
||||
@objc private func applicationWillEnterForeground(notification: NSNotification) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
self.reportedApplicationState = .inactive
|
||||
OWSLogger.info("")
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: .OWSApplicationWillEnterForeground,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
|
||||
@objc private func applicationDidEnterBackground(notification: NSNotification) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
self.reportedApplicationState = .background
|
||||
|
||||
OWSLogger.info("")
|
||||
DDLog.flushLog()
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: .OWSApplicationDidEnterBackground,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
|
||||
@objc private func applicationWillResignActive(notification: NSNotification) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
self.reportedApplicationState = .inactive
|
||||
|
||||
OWSLogger.info("")
|
||||
DDLog.flushLog()
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: .OWSApplicationWillResignActive,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
|
||||
@objc private func applicationDidBecomeActive(notification: NSNotification) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
self.reportedApplicationState = .active
|
||||
|
||||
OWSLogger.info("")
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: .OWSApplicationDidBecomeActive,
|
||||
object: nil
|
||||
)
|
||||
|
||||
self.runAppActiveBlocks()
|
||||
}
|
||||
|
||||
@objc private func applicationWillTerminate(notification: NSNotification) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
OWSLogger.info("")
|
||||
DDLog.flushLog()
|
||||
}
|
||||
|
||||
// MARK: - AppContext Functions
|
||||
|
||||
func setStatusBarHidden(_ isHidden: Bool, animated isAnimated: Bool) {
|
||||
UIApplication.shared.setStatusBarHidden(isHidden, with: (isAnimated ? .slide : .none))
|
||||
}
|
||||
|
||||
func isAppForegroundAndActive() -> Bool {
|
||||
return (reportedApplicationState == .active)
|
||||
}
|
||||
|
||||
func isInBackground() -> Bool {
|
||||
return (reportedApplicationState == .background)
|
||||
}
|
||||
|
||||
func beginBackgroundTask(expirationHandler: @escaping BackgroundTaskExpirationHandler) -> UIBackgroundTaskIdentifier {
|
||||
return UIApplication.shared.beginBackgroundTask(expirationHandler: expirationHandler)
|
||||
}
|
||||
|
||||
func endBackgroundTask(_ backgroundTaskIdentifier: UIBackgroundTaskIdentifier) {
|
||||
UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier)
|
||||
}
|
||||
|
||||
func ensureSleepBlocking(_ shouldBeBlocking: Bool, blockingObjects: [Any]) {
|
||||
if UIApplication.shared.isIdleTimerDisabled != shouldBeBlocking {
|
||||
if shouldBeBlocking {
|
||||
var logString: String = "Blocking sleep because of: \(String(describing: blockingObjects.first))"
|
||||
|
||||
if blockingObjects.count > 1 {
|
||||
logString = "\(logString) (and \(blockingObjects.count - 1) others)"
|
||||
}
|
||||
OWSLogger.info(logString)
|
||||
}
|
||||
else {
|
||||
OWSLogger.info("Unblocking Sleep.")
|
||||
}
|
||||
}
|
||||
UIApplication.shared.isIdleTimerDisabled = shouldBeBlocking
|
||||
}
|
||||
|
||||
func setMainAppBadgeNumber(_ value: Int) {
|
||||
UIApplication.shared.applicationIconBadgeNumber = value
|
||||
UserDefaults.sharedLokiProject?.setValue(value, forKey: "currentBadgeNumber")
|
||||
}
|
||||
|
||||
func frontmostViewController() -> UIViewController? {
|
||||
UIApplication.shared.frontmostViewControllerIgnoringAlerts
|
||||
}
|
||||
|
||||
func setNetworkActivityIndicatorVisible(_ value: Bool) {
|
||||
UIApplication.shared.isNetworkActivityIndicatorVisible = value
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
func runNowOr(whenMainAppIsActive block: @escaping AppActiveBlock) {
|
||||
Threading.dispatchMainThreadSafe { [weak self] in
|
||||
if self?.isMainAppAndActive == true {
|
||||
// App active blocks typically will be used to safely access the
|
||||
// shared data container, so use a background task to protect this
|
||||
// work.
|
||||
var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(label: #function)
|
||||
block()
|
||||
if backgroundTask != nil { backgroundTask = nil }
|
||||
return
|
||||
}
|
||||
|
||||
self?.appActiveBlocks.append(block)
|
||||
}
|
||||
}
|
||||
|
||||
func runAppActiveBlocks() {
|
||||
// App active blocks typically will be used to safely access the
|
||||
// shared data container, so use a background task to protect this
|
||||
// work.
|
||||
var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(label: #function)
|
||||
|
||||
let appActiveBlocks: [AppActiveBlock] = self.appActiveBlocks
|
||||
self.appActiveBlocks.removeAll()
|
||||
|
||||
appActiveBlocks.forEach { $0() }
|
||||
if backgroundTask != nil { backgroundTask = nil }
|
||||
}
|
||||
|
||||
func appDocumentDirectoryPath() -> String {
|
||||
let targetPath: String? = FileManager.default
|
||||
.urls(
|
||||
for: .documentDirectory,
|
||||
in: .userDomainMask
|
||||
)
|
||||
.last?
|
||||
.path
|
||||
owsAssertDebug(targetPath != nil)
|
||||
|
||||
return (targetPath ?? "")
|
||||
}
|
||||
|
||||
func appSharedDataDirectoryPath() -> String {
|
||||
let targetPath: String? = FileManager.default
|
||||
.containerURL(forSecurityApplicationGroupIdentifier: UserDefaults.applicationGroup)?
|
||||
.path
|
||||
owsAssertDebug(targetPath != nil)
|
||||
|
||||
return (targetPath ?? "")
|
||||
}
|
||||
|
||||
func appUserDefaults() -> UserDefaults {
|
||||
owsAssertDebug(UserDefaults.sharedLokiProject != nil)
|
||||
|
||||
return (UserDefaults.sharedLokiProject ?? UserDefaults.standard)
|
||||
}
|
||||
}
|
|
@ -7,4 +7,3 @@
|
|||
#import "OWSBezierPathView.h"
|
||||
#import "OWSMessageTimerView.h"
|
||||
#import "OWSWindowManager.h"
|
||||
#import "MainAppContext.h"
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -648,3 +648,4 @@
|
|||
"USER_CONFIG_OUTDATED_WARNING" = "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.";
|
||||
"LOAD_RECOVERY_PASSWORD_ERROR" = "An error occurred when trying to load your recovery password.\n\nPlease export your logs, then upload the file though Session's Help Desk to help resolve this issue.";
|
||||
"FAILED_TO_STORE_OUTGOING_MESSAGE" = "An error occurred when trying to store the outgoing message for sending, you may need to restart the app before you can send messages.";
|
||||
"database_inaccessible_error" = "There is an issue opening the database. Please restart the app and try again.";
|
||||
|
|
|
@ -6,6 +6,8 @@ import GRDB
|
|||
import SessionMessagingKit
|
||||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
import SessionSnodeKit
|
||||
|
||||
/// There are two primary components in our system notification integration:
|
||||
///
|
||||
|
|
|
@ -4,8 +4,10 @@ import Foundation
|
|||
import Combine
|
||||
import PushKit
|
||||
import GRDB
|
||||
import SessionMessagingKit
|
||||
import SignalUtilitiesKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public enum PushRegistrationError: Error {
|
||||
case assertionError(description: String)
|
||||
|
@ -53,8 +55,6 @@ public enum PushRegistrationError: Error {
|
|||
Logger.info("")
|
||||
|
||||
return registerUserNotificationSettings()
|
||||
.subscribe(on: DispatchQueue.global(qos: .default))
|
||||
.receive(on: DispatchQueue.main) // MUST be on main thread
|
||||
.setFailureType(to: Error.self)
|
||||
.tryFlatMap { _ -> AnyPublisher<(pushToken: String, voipToken: String), Error> in
|
||||
#if targetEnvironment(simulator)
|
||||
|
@ -75,24 +75,27 @@ public enum PushRegistrationError: Error {
|
|||
// MARK: Vanilla push token
|
||||
|
||||
// Vanilla push token is obtained from the system via AppDelegate
|
||||
public func didReceiveVanillaPushToken(_ tokenData: Data) {
|
||||
public func didReceiveVanillaPushToken(_ tokenData: Data, using dependencies: Dependencies = Dependencies()) {
|
||||
guard let vanillaTokenResolver = self.vanillaTokenResolver else {
|
||||
owsFailDebug("publisher completion in \(#function) unexpectedly nil")
|
||||
return
|
||||
}
|
||||
|
||||
vanillaTokenResolver(Result.success(tokenData))
|
||||
DispatchQueue.global(qos: .default).async(using: dependencies) {
|
||||
vanillaTokenResolver(Result.success(tokenData))
|
||||
}
|
||||
}
|
||||
|
||||
// Vanilla push token is obtained from the system via AppDelegate
|
||||
@objc
|
||||
public func didFailToReceiveVanillaPushToken(error: Error) {
|
||||
public func didFailToReceiveVanillaPushToken(error: Error, using dependencies: Dependencies = Dependencies()) {
|
||||
guard let vanillaTokenResolver = self.vanillaTokenResolver else {
|
||||
owsFailDebug("publisher completion in \(#function) unexpectedly nil")
|
||||
return
|
||||
}
|
||||
|
||||
vanillaTokenResolver(Result.failure(error))
|
||||
DispatchQueue.global(qos: .default).async(using: dependencies) {
|
||||
vanillaTokenResolver(Result.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: helpers
|
||||
|
@ -111,9 +114,8 @@ public enum PushRegistrationError: Error {
|
|||
* in this case we've verified that we *have* properly registered notification settings.
|
||||
*/
|
||||
private var isSusceptibleToFailedPushRegistration: Bool {
|
||||
|
||||
// Only affects users who have disabled both: background refresh *and* notifications
|
||||
guard UIApplication.shared.backgroundRefreshStatus == .denied else {
|
||||
guard DispatchQueue.main.sync(execute: { UIApplication.shared.backgroundRefreshStatus }) == .denied else {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -128,10 +130,7 @@ public enum PushRegistrationError: Error {
|
|||
return true
|
||||
}
|
||||
|
||||
// FIXME: Might be nice to try to avoid having this required to run on the main thread (follow a similar approach to the 'SyncPushTokensJob' & `Atomic<T>`?)
|
||||
private func registerForVanillaPushToken() -> AnyPublisher<String, Error> {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
// Use the existing publisher if it exists
|
||||
if let vanillaTokenPublisher: AnyPublisher<Data, Error> = self.vanillaTokenPublisher {
|
||||
return vanillaTokenPublisher
|
||||
|
@ -139,19 +138,23 @@ public enum PushRegistrationError: Error {
|
|||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
UIApplication.shared.registerForRemoteNotifications()
|
||||
|
||||
// No pending vanilla token yet; create a new publisher
|
||||
let publisher: AnyPublisher<Data, Error> = Deferred {
|
||||
Future<Data, Error> { self.vanillaTokenResolver = $0 }
|
||||
Future<Data, Error> {
|
||||
self.vanillaTokenResolver = $0
|
||||
|
||||
// Tell the device to register for remote notifications
|
||||
DispatchQueue.main.sync { UIApplication.shared.registerForRemoteNotifications() }
|
||||
}
|
||||
}
|
||||
.shareReplay(1)
|
||||
.eraseToAnyPublisher()
|
||||
self.vanillaTokenPublisher = publisher
|
||||
|
||||
return publisher
|
||||
.timeout(
|
||||
.seconds(10),
|
||||
scheduler: DispatchQueue.main,
|
||||
scheduler: DispatchQueue.global(qos: .default),
|
||||
customError: { PushRegistrationError.timeout }
|
||||
)
|
||||
.catch { error -> AnyPublisher<Data, Error> in
|
||||
|
@ -200,9 +203,8 @@ public enum PushRegistrationError: Error {
|
|||
}
|
||||
|
||||
public func createVoipRegistryIfNecessary() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard voipRegistry == nil else { return }
|
||||
|
||||
let voipRegistry = PKPushRegistry(queue: nil)
|
||||
self.voipRegistry = voipRegistry
|
||||
voipRegistry.desiredPushTypes = [.voIP]
|
||||
|
@ -210,8 +212,6 @@ public enum PushRegistrationError: Error {
|
|||
}
|
||||
|
||||
private func registerForVoipPushToken() -> AnyPublisher<String?, Error> {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
// Use the existing publisher if it exists
|
||||
if let voipTokenPublisher: AnyPublisher<Data?, Error> = self.voipTokenPublisher {
|
||||
return voipTokenPublisher
|
||||
|
|
|
@ -31,59 +31,39 @@ public enum SyncPushTokensJob: JobExecutor {
|
|||
return deferred(job, dependencies)
|
||||
}
|
||||
|
||||
// We need to check a UIApplication setting which needs to run on the main thread so synchronously
|
||||
// retrieve the value so we can continue
|
||||
let isRegisteredForRemoteNotifications: Bool = {
|
||||
guard !Thread.isMainThread else {
|
||||
return UIApplication.shared.isRegisteredForRemoteNotifications
|
||||
}
|
||||
|
||||
return DispatchQueue.main.sync {
|
||||
return UIApplication.shared.isRegisteredForRemoteNotifications
|
||||
}
|
||||
}()
|
||||
|
||||
// Apple's documentation states that we should re-register for notifications on every launch:
|
||||
// https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/HandlingRemoteNotifications.html#//apple_ref/doc/uid/TP40008194-CH6-SW1
|
||||
guard job.behaviour == .runOnce || !isRegisteredForRemoteNotifications else {
|
||||
SNLog("[SyncPushTokensJob] Deferred due to Fast Mode disabled")
|
||||
deferred(job, dependencies) // Don't need to do anything if push notifications are already registered
|
||||
return
|
||||
}
|
||||
|
||||
// Determine if the device has 'Fast Mode' (APNS) enabled
|
||||
let isUsingFullAPNs: Bool = UserDefaults.standard[.isUsingFullAPNs]
|
||||
|
||||
// If the job is running and 'Fast Mode' is disabled then we should try to unregister the existing
|
||||
// token
|
||||
guard isUsingFullAPNs else {
|
||||
Just(Storage.shared[.lastRecordedPushToken])
|
||||
Just(dependencies.storage[.lastRecordedPushToken])
|
||||
.setFailureType(to: Error.self)
|
||||
.flatMap { lastRecordedPushToken in
|
||||
.flatMap { lastRecordedPushToken -> AnyPublisher<Void, Error> in
|
||||
// Tell the device to unregister for remote notifications (essentially try to invalidate
|
||||
// the token if needed - we do this first to avoid wrid race conditions which could be
|
||||
// triggered by the user immediately re-registering)
|
||||
DispatchQueue.main.sync { UIApplication.shared.unregisterForRemoteNotifications() }
|
||||
|
||||
// Clear the old token
|
||||
dependencies.storage.write(using: dependencies) { db in
|
||||
db[.lastRecordedPushToken] = nil
|
||||
}
|
||||
|
||||
// Unregister from our server
|
||||
if let existingToken: String = lastRecordedPushToken {
|
||||
SNLog("[SyncPushTokensJob] Unregister using last recorded push token: \(redact(existingToken))")
|
||||
return Just(existingToken)
|
||||
.setFailureType(to: Error.self)
|
||||
return PushNotificationAPI.unsubscribe(token: Data(hex: existingToken))
|
||||
.map { _ in () }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
SNLog("[SyncPushTokensJob] Unregister using live token provided from device")
|
||||
return PushRegistrationManager.shared.requestPushTokens()
|
||||
.map { token, _ in token }
|
||||
SNLog("[SyncPushTokensJob] No previous token stored just triggering device unregister")
|
||||
return Just(())
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
.flatMap { pushToken in PushNotificationAPI.unregister(Data(hex: pushToken)) }
|
||||
.map {
|
||||
// Tell the device to unregister for remote notifications (essentially try to invalidate
|
||||
// the token if needed
|
||||
DispatchQueue.main.sync { UIApplication.shared.unregisterForRemoteNotifications() }
|
||||
|
||||
Storage.shared.write { db in
|
||||
db[.lastRecordedPushToken] = nil
|
||||
}
|
||||
return ()
|
||||
}
|
||||
.subscribe(on: queue)
|
||||
.subscribe(on: queue, using: dependencies)
|
||||
.sinkUntilComplete(
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
|
@ -98,17 +78,20 @@ public enum SyncPushTokensJob: JobExecutor {
|
|||
return
|
||||
}
|
||||
|
||||
// Perform device registration
|
||||
/// Perform device registration
|
||||
///
|
||||
/// **Note:** Apple's documentation states that we should re-register for notifications on every launch:
|
||||
/// https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/HandlingRemoteNotifications.html#//apple_ref/doc/uid/TP40008194-CH6-SW1
|
||||
Logger.info("Re-registering for remote notifications.")
|
||||
PushRegistrationManager.shared.requestPushTokens()
|
||||
.flatMap { (pushToken: String, voipToken: String) -> AnyPublisher<Void, Error> in
|
||||
PushNotificationAPI
|
||||
.register(
|
||||
with: Data(hex: pushToken),
|
||||
publicKey: getUserHexEncodedPublicKey(),
|
||||
isForcedUpdate: true
|
||||
.subscribe(
|
||||
token: Data(hex: pushToken),
|
||||
isForcedUpdate: true,
|
||||
using: dependencies
|
||||
)
|
||||
.retry(3)
|
||||
.retry(3, using: dependencies)
|
||||
.handleEvents(
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
|
@ -118,9 +101,9 @@ public enum SyncPushTokensJob: JobExecutor {
|
|||
case .finished:
|
||||
Logger.warn("Recording push tokens locally. pushToken: \(redact(pushToken)), voipToken: \(redact(voipToken))")
|
||||
SNLog("[SyncPushTokensJob] Completed")
|
||||
UserDefaults.standard[.lastPushNotificationSync] = Date()
|
||||
dependencies.standardUserDefaults[.lastPushNotificationSync] = dependencies.dateNow
|
||||
|
||||
Storage.shared.write { db in
|
||||
dependencies.storage.write(using: dependencies) { db in
|
||||
db[.lastRecordedPushToken] = pushToken
|
||||
db[.lastRecordedVoipToken] = voipToken
|
||||
}
|
||||
|
@ -130,7 +113,7 @@ public enum SyncPushTokensJob: JobExecutor {
|
|||
.map { _ in () }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
.subscribe(on: queue)
|
||||
.subscribe(on: queue, using: dependencies)
|
||||
.sinkUntilComplete(
|
||||
// We want to complete this job regardless of success or failure
|
||||
receiveCompletion: { _ in success(job, false, dependencies) }
|
||||
|
@ -168,5 +151,9 @@ extension SyncPushTokensJob {
|
|||
// MARK: - Convenience
|
||||
|
||||
private func redact(_ string: String) -> String {
|
||||
return OWSIsDebugBuild() ? string : "[ READACTED \(string.prefix(2))...\(string.suffix(2)) ]"
|
||||
#if DEBUG
|
||||
return string
|
||||
#else
|
||||
return "[ READACTED \(string.prefix(2))...\(string.suffix(2)) ]"
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import UserNotifications
|
|||
import SessionMessagingKit
|
||||
import SignalCoreKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
class UserNotificationConfig {
|
||||
|
||||
|
|
|
@ -251,9 +251,9 @@ enum Onboarding {
|
|||
// Notify the app that registration is complete
|
||||
Identity.didRegister()
|
||||
|
||||
// Now that we have registered get the Snode pool and sync push tokens
|
||||
// Now that we have registered get the Snode pool (just in case) - other non-blocking
|
||||
// launch jobs will automatically be run because the app activation was triggered
|
||||
GetSnodePoolJob.run()
|
||||
SyncPushTokensJob.run(uploadOnlyIfStale: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import SessionUIKit
|
|||
import SessionMessagingKit
|
||||
import SessionSnodeKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class PNModeVC: BaseVC, OptionViewDelegate {
|
||||
private let flow: Onboarding.Flow
|
||||
|
|
|
@ -4,6 +4,7 @@ import UIKit
|
|||
import Sodium
|
||||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class RegisterVC : BaseVC {
|
||||
private var seed: Data! { didSet { updateKeyPair() } }
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import UIKit
|
||||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class AppearanceViewController: BaseVC {
|
||||
// MARK: - Components
|
||||
|
|
|
@ -6,6 +6,7 @@ import GRDB
|
|||
import DifferenceKit
|
||||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsViewModel.Section, Profile> {
|
||||
// MARK: - Section
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionUtilitiesKit
|
||||
|
||||
class ImagePickerHandler: NSObject, UIImagePickerControllerDelegate & UINavigationControllerDelegate {
|
||||
private let onTransition: (UIViewController, TransitionType) -> Void
|
||||
|
|
|
@ -5,6 +5,7 @@ import SessionUIKit
|
|||
import SessionSnodeKit
|
||||
import SessionMessagingKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class NukeDataModal: Modal {
|
||||
// MARK: - Initialization
|
||||
|
@ -226,8 +227,9 @@ final class NukeDataModal: Modal {
|
|||
let maybeDeviceToken: String? = UserDefaults.standard[.deviceToken]
|
||||
|
||||
if isUsingFullAPNs, let deviceToken: String = maybeDeviceToken {
|
||||
let data: Data = Data(hex: deviceToken)
|
||||
PushNotificationAPI.unregister(data).sinkUntilComplete()
|
||||
PushNotificationAPI
|
||||
.unsubscribe(token: Data(hex: deviceToken))
|
||||
.sinkUntilComplete()
|
||||
}
|
||||
|
||||
/// Stop and cancel all current jobs (don't want to inadvertantly have a job store data after it's table has already been cleared)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public protocol CaptionContainerViewDelegate: AnyObject {
|
||||
func captionContainerViewDidUpdateText(_ captionContainerView: CaptionContainerView)
|
||||
|
|
|
@ -4,6 +4,7 @@ import UIKit
|
|||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public final class FullConversationCell: UITableViewCell, SwipeActionOptimisticCell {
|
||||
public static let mutePrefix: String = "\u{e067} "
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
|
||||
// MARK: - Main Types
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Foundation
|
||||
import GRDB
|
||||
import SessionSnodeKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class IP2Country {
|
||||
static var isInitialized = false
|
||||
|
@ -12,16 +13,16 @@ final class IP2Country {
|
|||
/// the **lower** bound of an IP range and the "registered_country_geoname_id" column contains the ID of the country corresponding
|
||||
/// to that range. We look up an IP by finding the first index in the network column where the value is greater than the IP we're looking
|
||||
/// up (converted to an integer). The IP we're looking up must then be in the range **before** that range.
|
||||
private lazy var ipv4Table: [String:[Int]] = {
|
||||
private lazy var ipv4Table: [String: [Int]] = {
|
||||
let url = Bundle.main.url(forResource: "GeoLite2-Country-Blocks-IPv4", withExtension: nil)!
|
||||
let data = try! Data(contentsOf: url)
|
||||
return try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! [String:[Int]]
|
||||
return try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! [String: [Int]]
|
||||
}()
|
||||
|
||||
private lazy var countryNamesTable: [String:[String]] = {
|
||||
private lazy var countryNamesTable: [String: [String]] = {
|
||||
let url = Bundle.main.url(forResource: "GeoLite2-Country-Locations-English", withExtension: nil)!
|
||||
let data = try! Data(contentsOf: url)
|
||||
return try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! [String:[String]]
|
||||
return try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! [String: [String]]
|
||||
}()
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
|
|
@ -4,6 +4,7 @@ import Foundation
|
|||
import GRDB
|
||||
import Curve25519Kit
|
||||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
enum MockDataGenerator {
|
||||
// MARK: - Generation
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import UIKit
|
||||
import SessionMessagingKit
|
||||
import SessionUIKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
protocol SwipeActionOptimisticCell {
|
||||
func optimisticUpdate(isMuted: Bool?, isBlocked: Bool?, isPinned: Bool?, hasUnread: Bool?)
|
||||
|
|
|
@ -144,10 +144,9 @@ public extension ClosedGroup {
|
|||
ClosedGroupPoller.shared.stopPolling(for: threadId)
|
||||
|
||||
PushNotificationAPI
|
||||
.performOperation(
|
||||
.unsubscribe,
|
||||
for: threadId,
|
||||
publicKey: userPublicKey
|
||||
.unsubscribeFromLegacyGroup(
|
||||
legacyGroupId: threadId,
|
||||
currentUserPublicKey: userPublicKey
|
||||
)
|
||||
.sinkUntilComplete()
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import Combine
|
|||
import SessionSnodeKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
// FIXME: Remove this once legacy notifications and legacy groups are deprecated
|
||||
public enum NotifyPushServerJob: JobExecutor {
|
||||
public static var maxFailureCount: Int = 20
|
||||
public static var requiresThreadId: Bool = false
|
||||
|
@ -27,7 +28,7 @@ public enum NotifyPushServerJob: JobExecutor {
|
|||
}
|
||||
|
||||
PushNotificationAPI
|
||||
.notify(
|
||||
.legacyNotify(
|
||||
recipient: details.message.recipient,
|
||||
with: details.message.data,
|
||||
maxRetryCount: 4
|
||||
|
|
|
@ -97,7 +97,7 @@ extension OpenGroupAPI.Message {
|
|||
throw HTTPError.parsingFailed
|
||||
}
|
||||
|
||||
case .none:
|
||||
case .none, .group:
|
||||
SNLog("Ignoring message with invalid sender.")
|
||||
throw HTTPError.parsingFailed
|
||||
}
|
||||
|
|
|
@ -976,6 +976,8 @@ public final class OpenGroupManager {
|
|||
.filter(possibleKeys.contains(GroupMember.Columns.profileId))
|
||||
.filter(targetRoles.contains(GroupMember.Columns.role))
|
||||
.isNotEmpty(db)
|
||||
|
||||
case .group: return false
|
||||
}
|
||||
}
|
||||
.defaulting(to: false)
|
||||
|
|
|
@ -231,8 +231,24 @@ extension MessageReceiver {
|
|||
// Start polling
|
||||
ClosedGroupPoller.shared.startIfNeeded(for: groupPublicKey, using: dependencies)
|
||||
|
||||
// Notify the PN server
|
||||
let _ = PushNotificationAPI.performOperation(.subscribe, for: groupPublicKey, publicKey: getUserHexEncodedPublicKey(db))
|
||||
// Resubscribe for group push notifications
|
||||
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
|
||||
PushNotificationAPI
|
||||
.subscribeToLegacyGroups(
|
||||
currentUserPublicKey: currentUserPublicKey,
|
||||
legacyGroupIds: try ClosedGroup
|
||||
.select(.threadId)
|
||||
.filter(!ClosedGroup.Columns.threadId.like("\(SessionId.Prefix.group.rawValue)%"))
|
||||
.joining(
|
||||
required: ClosedGroup.members
|
||||
.filter(GroupMember.Columns.profileId == currentUserPublicKey)
|
||||
)
|
||||
.asRequest(of: String.self)
|
||||
.fetchSet(db)
|
||||
.inserting(groupPublicKey) // Insert the new key just to be sure
|
||||
)
|
||||
.sinkUntilComplete()
|
||||
}
|
||||
|
||||
/// Extracts and adds the new encryption key pair to our list of key pairs if there is one for our public key, AND the message was
|
||||
|
|
|
@ -73,7 +73,7 @@ extension MessageReceiver {
|
|||
|
||||
return try? OpenGroup.fetchOne(db, id: threadId)
|
||||
}()
|
||||
let variant: Interaction.Variant = {
|
||||
let variant: Interaction.Variant = try {
|
||||
guard
|
||||
let senderSessionId: SessionId = SessionId(from: sender),
|
||||
let openGroup: OpenGroup = maybeOpenGroup
|
||||
|
@ -115,6 +115,10 @@ extension MessageReceiver {
|
|||
.standardOutgoing :
|
||||
.standardIncoming
|
||||
)
|
||||
|
||||
case .group:
|
||||
SNLog("Ignoring message with invalid sender.")
|
||||
throw HTTPError.parsingFailed
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ extension MessageSender {
|
|||
using dependencies: Dependencies = Dependencies()
|
||||
) -> AnyPublisher<SessionThread, Error> {
|
||||
dependencies.storage
|
||||
.writePublisher { db -> (String, SessionThread, [MessageSender.PreparedSendData]) in
|
||||
.writePublisher { db -> (String, SessionThread, [MessageSender.PreparedSendData], Set<String>) in
|
||||
// Generate the group's two keys
|
||||
guard
|
||||
let groupKeyPair: KeyPair = dependencies.crypto.generate(.x25519KeyPair()),
|
||||
|
@ -108,21 +108,30 @@ extension MessageSender {
|
|||
using: dependencies
|
||||
)
|
||||
}
|
||||
let allActiveLegacyGroupIds: Set<String> = try ClosedGroup
|
||||
.select(.threadId)
|
||||
.filter(!ClosedGroup.Columns.threadId.like("\(SessionId.Prefix.group.rawValue)%"))
|
||||
.joining(
|
||||
required: ClosedGroup.members
|
||||
.filter(GroupMember.Columns.profileId == userPublicKey)
|
||||
)
|
||||
.asRequest(of: String.self)
|
||||
.fetchSet(db)
|
||||
.inserting(groupPublicKey) // Insert the new key just to be sure
|
||||
|
||||
return (userPublicKey, thread, memberSendData)
|
||||
return (userPublicKey, thread, memberSendData, allActiveLegacyGroupIds)
|
||||
}
|
||||
.flatMap { userPublicKey, thread, memberSendData in
|
||||
.flatMap { userPublicKey, thread, memberSendData, allActiveLegacyGroupIds in
|
||||
Publishers
|
||||
.MergeMany(
|
||||
// Send a closed group update message to all members individually
|
||||
memberSendData
|
||||
.map { MessageSender.sendImmediate(data: $0, using: dependencies) }
|
||||
.appending(
|
||||
// Notify the PN server
|
||||
PushNotificationAPI.performOperation(
|
||||
.subscribe,
|
||||
for: thread.id,
|
||||
publicKey: userPublicKey
|
||||
// Resubscribe to all legacy groups
|
||||
PushNotificationAPI.subscribeToLegacyGroups(
|
||||
currentUserPublicKey: userPublicKey,
|
||||
legacyGroupIds: allActiveLegacyGroupIds
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -65,6 +65,11 @@ public enum MessageReceiver {
|
|||
userEd25519KeyPair: userEd25519KeyPair,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .group:
|
||||
// TODO: Need to decide how we will handle updated group messages
|
||||
SNLog("Ignoring message with invalid sender.")
|
||||
throw HTTPError.parsingFailed
|
||||
}
|
||||
|
||||
case .closedGroupMessage:
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension PushNotificationAPI {
|
||||
struct LegacyGroupOnlyRequest: Codable {
|
||||
let token: String
|
||||
let pubKey: String
|
||||
let device: String
|
||||
let legacyGroupPublicKeys: Set<String>
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension PushNotificationAPI {
|
||||
struct LegacyGroupRequest: Codable {
|
||||
let pubKey: String
|
||||
let closedGroupPublicKey: String
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension PushNotificationAPI {
|
||||
struct LegacyNotifyRequest: Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case data
|
||||
case sendTo = "send_to"
|
||||
}
|
||||
|
||||
let data: String
|
||||
let sendTo: String
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
import Foundation
|
||||
|
||||
extension PushNotificationAPI {
|
||||
struct PushServerResponse: Codable {
|
||||
struct LegacyPushServerResponse: Codable {
|
||||
let code: Int
|
||||
let message: String?
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionSnodeKit
|
||||
|
||||
extension PushNotificationAPI {
|
||||
struct LegacyUnsubscribeRequest: Codable {
|
||||
private let token: String
|
||||
|
||||
init(token: String) {
|
||||
self.token = token
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension PushNotificationAPI {
|
||||
struct NotificationMetadata: Codable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case accountId = "@"
|
||||
case hash = "#"
|
||||
case namespace = "n"
|
||||
case dataLength = "l"
|
||||
case dataTooLong = "B"
|
||||
}
|
||||
|
||||
/// Account ID (such as Session ID or closed group ID) where the message arrived.
|
||||
let accountId: String
|
||||
|
||||
/// The hash of the message in the swarm.
|
||||
let hash: String
|
||||
|
||||
/// The swarm namespace in which this message arrived.
|
||||
let namespace: Int
|
||||
|
||||
/// The length of the message data. This is always included, even if the message content
|
||||
/// itself was too large to fit into the push notification.
|
||||
let dataLength: Int
|
||||
|
||||
/// This will be `true` if the data was omitted because it was too long to fit in a push
|
||||
/// notification (around 2.5kB of raw data), in which case the push notification includes
|
||||
/// only this metadata but not the message content itself.
|
||||
let dataTooLong: Bool
|
||||
}
|
||||
}
|
||||
|
||||
extension PushNotificationAPI.NotificationMetadata {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self = PushNotificationAPI.NotificationMetadata(
|
||||
accountId: try container.decode(String.self, forKey: .accountId),
|
||||
hash: try container.decode(String.self, forKey: .hash),
|
||||
namespace: try container.decode(Int.self, forKey: .namespace),
|
||||
dataLength: try container.decode(Int.self, forKey: .dataLength),
|
||||
dataTooLong: ((try? container.decode(Bool.self, forKey: .dataTooLong)) ?? false)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public struct PushNotificationAPIRequest<T: Encodable>: Encodable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case method
|
||||
case body = "params"
|
||||
}
|
||||
|
||||
internal let endpoint: PushNotificationAPI.Endpoint
|
||||
internal let body: T
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
public init(
|
||||
endpoint: PushNotificationAPI.Endpoint,
|
||||
body: T
|
||||
) {
|
||||
self.endpoint = endpoint
|
||||
self.body = body
|
||||
}
|
||||
|
||||
// MARK: - Codable
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(endpoint.rawValue, forKey: .method)
|
||||
try container.encode(body, forKey: .body)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionSnodeKit
|
||||
|
||||
extension PushNotificationAPI {
|
||||
struct SubscribeRequest: Encodable {
|
||||
struct ServiceInfo: Codable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case token
|
||||
}
|
||||
|
||||
private let token: String
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
init(token: String) {
|
||||
self.token = token
|
||||
}
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case pubkey
|
||||
case ed25519PublicKey = "session_ed25519"
|
||||
case subkey = "subkey_tag"
|
||||
case namespaces
|
||||
case includeMessageData = "data"
|
||||
case timestamp = "sig_ts"
|
||||
case signatureBase64 = "signature"
|
||||
case service
|
||||
case serviceInfo = "service_info"
|
||||
case notificationsEncryptionKey = "enc_key"
|
||||
}
|
||||
|
||||
/// The 33-byte account being subscribed to; typically a session ID.
|
||||
private let pubkey: String
|
||||
|
||||
/// List of integer namespace (-32768 through 32767). These must be sorted in ascending order.
|
||||
private let namespaces: [SnodeAPI.Namespace]
|
||||
|
||||
/// If provided and true then notifications will include the body of the message (as long as it isn't too large); if false then the body will
|
||||
/// not be included in notifications.
|
||||
private let includeMessageData: Bool
|
||||
|
||||
/// Dict of service-specific data; typically this includes just a "token" field with a device-specific token, but different services in the
|
||||
/// future may have different input requirements.
|
||||
private let serviceInfo: ServiceInfo
|
||||
|
||||
/// 32-byte encryption key; notification payloads sent to the device will be encrypted with XChaCha20-Poly1305 using this key. Though
|
||||
/// it is permitted for this to change, it is recommended that the device generate this once and persist it.
|
||||
private let notificationsEncryptionKey: Data
|
||||
|
||||
/// 32-byte swarm authentication subkey; omitted (or null) when not using subkey auth
|
||||
private let subkey: String?
|
||||
|
||||
/// The signature unix timestamp (seconds, not ms)
|
||||
private let timestamp: Int64
|
||||
|
||||
/// When the pubkey value starts with 05 (i.e. a session ID) this is the underlying ed25519 32-byte pubkey associated with the session
|
||||
/// ID. When not 05, this field should not be provided.
|
||||
private let ed25519PublicKey: [UInt8]
|
||||
|
||||
/// Secret key used to generate the signature (**Not** sent with the request)
|
||||
private let ed25519SecretKey: [UInt8]
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
init(
|
||||
pubkey: String,
|
||||
namespaces: [SnodeAPI.Namespace],
|
||||
includeMessageData: Bool,
|
||||
serviceInfo: ServiceInfo,
|
||||
notificationsEncryptionKey: Data,
|
||||
subkey: String?,
|
||||
timestamp: TimeInterval,
|
||||
ed25519PublicKey: [UInt8],
|
||||
ed25519SecretKey: [UInt8]
|
||||
) {
|
||||
self.pubkey = pubkey
|
||||
self.namespaces = namespaces
|
||||
self.includeMessageData = includeMessageData
|
||||
self.serviceInfo = serviceInfo
|
||||
self.notificationsEncryptionKey = notificationsEncryptionKey
|
||||
self.subkey = subkey
|
||||
self.timestamp = Int64(timestamp) // Server expects rounded seconds
|
||||
self.ed25519PublicKey = ed25519PublicKey
|
||||
self.ed25519SecretKey = ed25519SecretKey
|
||||
}
|
||||
|
||||
// MARK: - Coding
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
// Generate the signature for the request for encoding
|
||||
let signatureBase64: String = try generateSignature().toBase64()
|
||||
try container.encode(pubkey, forKey: .pubkey)
|
||||
try container.encode(ed25519PublicKey.toHexString(), forKey: .ed25519PublicKey)
|
||||
try container.encodeIfPresent(subkey, forKey: .subkey)
|
||||
try container.encode(namespaces.map { $0.rawValue}.sorted(), forKey: .namespaces)
|
||||
try container.encode(includeMessageData, forKey: .includeMessageData)
|
||||
try container.encode(timestamp, forKey: .timestamp)
|
||||
try container.encode(signatureBase64, forKey: .signatureBase64)
|
||||
try container.encode(Service.apns, forKey: .service)
|
||||
try container.encode(serviceInfo, forKey: .serviceInfo)
|
||||
try container.encode(notificationsEncryptionKey.toHexString(), forKey: .notificationsEncryptionKey)
|
||||
}
|
||||
|
||||
// MARK: - Abstract Methods
|
||||
|
||||
func generateSignature() throws -> [UInt8] {
|
||||
/// The signature data collected and stored here is used by the PN server to subscribe to the swarms
|
||||
/// for the given account; the specific rules are governed by the storage server, but in general:
|
||||
///
|
||||
/// A signature must have been produced (via the timestamp) within the past 14 days. It is
|
||||
/// recommended that clients generate a new signature whenever they re-subscribe, and that
|
||||
/// re-subscriptions happen more frequently than once every 14 days.
|
||||
///
|
||||
/// A signature is signed using the account's Ed25519 private key (or Ed25519 subkey, if using
|
||||
/// subkey authentication with a `subkey_tag`, for future closed group subscriptions), and signs the value:
|
||||
/// `"MONITOR" || HEX(ACCOUNT) || SIG_TS || DATA01 || NS[0] || "," || ... || "," || NS[n]`
|
||||
///
|
||||
/// Where `SIG_TS` is the `sig_ts` value as a base-10 string; `DATA01` is either "0" or "1" depending
|
||||
/// on whether the subscription wants message data included; and the trailing `NS[i]` values are a
|
||||
/// comma-delimited list of namespaces that should be subscribed to, in the same sorted order as
|
||||
/// the `namespaces` parameter.
|
||||
let verificationBytes: [UInt8] = "MONITOR".bytes
|
||||
.appending(contentsOf: pubkey.bytes)
|
||||
.appending(contentsOf: "\(timestamp)".bytes)
|
||||
.appending(contentsOf: (includeMessageData ? "1" : "0").bytes)
|
||||
.appending(
|
||||
contentsOf: namespaces
|
||||
.map { $0.rawValue } // Intentionally not using `verificationString` here
|
||||
.sorted()
|
||||
.map { "\($0)" }
|
||||
.joined(separator: ",")
|
||||
.bytes
|
||||
)
|
||||
|
||||
// TODO: Need to add handling for subkey auth
|
||||
guard
|
||||
let signatureBytes: [UInt8] = sodium.wrappedValue.sign.signature(
|
||||
message: verificationBytes,
|
||||
secretKey: ed25519SecretKey
|
||||
)
|
||||
else {
|
||||
throw SnodeAPIError.signingFailed
|
||||
}
|
||||
|
||||
return signatureBytes
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension PushNotificationAPI {
|
||||
struct SubscribeResponse: Codable {
|
||||
/// Flag indicating the success of the registration
|
||||
let success: Bool?
|
||||
|
||||
/// Value is `true` upon an initial registration
|
||||
let added: Bool?
|
||||
|
||||
/// Value is `true` upon a renewal/update registration
|
||||
let updated: Bool?
|
||||
|
||||
/// This will be one of the errors found here:
|
||||
/// https://github.com/jagerman/session-push-notification-server/blob/spns-v2/spns/hive/subscription.hpp#L21
|
||||
///
|
||||
/// Values at the time of writing are:
|
||||
/// OK = 0 // Great Success!
|
||||
/// BAD_INPUT = 1 // Unparseable, invalid values, missing required arguments, etc. (details in the string)
|
||||
/// SERVICE_NOT_AVAILABLE = 2 // The requested service name isn't currently available
|
||||
/// SERVICE_TIMEOUT = 3 // The backend service did not response
|
||||
/// ERROR = 4 // There was some other error processing the subscription (details in the string)
|
||||
/// INTERNAL_ERROR = 5 // An internal program error occured processing the request
|
||||
let error: Int?
|
||||
|
||||
/// Includes additional information about the error
|
||||
let message: String?
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionSnodeKit
|
||||
|
||||
extension PushNotificationAPI {
|
||||
struct UnsubscribeRequest: Encodable {
|
||||
struct ServiceInfo: Codable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case token
|
||||
}
|
||||
|
||||
private let token: String
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
init(token: String) {
|
||||
self.token = token
|
||||
}
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case pubkey
|
||||
case ed25519PublicKey = "session_ed25519"
|
||||
case subkey = "subkey_tag"
|
||||
case timestamp = "sig_ts"
|
||||
case signatureBase64 = "signature"
|
||||
case service
|
||||
case serviceInfo = "service_info"
|
||||
}
|
||||
|
||||
/// The 33-byte account being subscribed to; typically a session ID.
|
||||
private let pubkey: String
|
||||
|
||||
/// Dict of service-specific data; typically this includes just a "token" field with a device-specific token, but different services in the
|
||||
/// future may have different input requirements.
|
||||
private let serviceInfo: ServiceInfo
|
||||
|
||||
/// 32-byte swarm authentication subkey; omitted (or null) when not using subkey auth
|
||||
private let subkey: String?
|
||||
|
||||
/// The signature unix timestamp (seconds, not ms)
|
||||
private let timestamp: Int64
|
||||
|
||||
/// When the pubkey value starts with 05 (i.e. a session ID) this is the underlying ed25519 32-byte pubkey associated with the session
|
||||
/// ID. When not 05, this field should not be provided.
|
||||
private let ed25519PublicKey: [UInt8]
|
||||
|
||||
/// Secret key used to generate the signature (**Not** sent with the request)
|
||||
private let ed25519SecretKey: [UInt8]
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
init(
|
||||
pubkey: String,
|
||||
serviceInfo: ServiceInfo,
|
||||
subkey: String?,
|
||||
timestamp: TimeInterval,
|
||||
ed25519PublicKey: [UInt8],
|
||||
ed25519SecretKey: [UInt8]
|
||||
) {
|
||||
self.pubkey = pubkey
|
||||
self.serviceInfo = serviceInfo
|
||||
self.subkey = subkey
|
||||
self.timestamp = Int64(timestamp) // Server expects rounded seconds
|
||||
self.ed25519PublicKey = ed25519PublicKey
|
||||
self.ed25519SecretKey = ed25519SecretKey
|
||||
}
|
||||
|
||||
// MARK: - Coding
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
// Generate the signature for the request for encoding
|
||||
let signatureBase64: String = try generateSignature().toBase64()
|
||||
try container.encode(pubkey, forKey: .pubkey)
|
||||
try container.encode(ed25519PublicKey.toHexString(), forKey: .ed25519PublicKey)
|
||||
try container.encodeIfPresent(subkey, forKey: .subkey)
|
||||
try container.encode(timestamp, forKey: .timestamp)
|
||||
try container.encode(signatureBase64, forKey: .signatureBase64)
|
||||
try container.encode(Service.apns, forKey: .service)
|
||||
try container.encode(serviceInfo, forKey: .serviceInfo)
|
||||
}
|
||||
|
||||
// MARK: - Abstract Methods
|
||||
|
||||
func generateSignature() throws -> [UInt8] {
|
||||
/// A signature is signed using the account's Ed25519 private key (or Ed25519 subkey, if using
|
||||
/// subkey authentication with a `subkey_tag`, for future closed group subscriptions), and signs the value:
|
||||
/// `"UNSUBSCRIBE" || HEX(ACCOUNT) || SIG_TS`
|
||||
///
|
||||
/// Where `SIG_TS` is the `sig_ts` value as a base-10 string and must be within 24 hours of the current time.
|
||||
let verificationBytes: [UInt8] = "UNSUBSCRIBE".bytes
|
||||
.appending(contentsOf: pubkey.bytes)
|
||||
.appending(contentsOf: "\(timestamp)".data(using: .ascii)?.bytes)
|
||||
|
||||
// TODO: Need to add handling for subkey auth
|
||||
guard
|
||||
let signatureBytes: [UInt8] = sodium.wrappedValue.sign.signature(
|
||||
message: verificationBytes,
|
||||
secretKey: ed25519SecretKey
|
||||
)
|
||||
else {
|
||||
throw SnodeAPIError.signingFailed
|
||||
}
|
||||
|
||||
return signatureBytes
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension PushNotificationAPI {
|
||||
struct UnsubscribeResponse: Codable {
|
||||
/// Flag indicating the success of the registration
|
||||
let success: Bool?
|
||||
|
||||
/// Value is `true` upon an initial registration
|
||||
let added: Bool?
|
||||
|
||||
/// Value is `true` upon a renewal/update registration
|
||||
let updated: Bool?
|
||||
|
||||
/// This will be one of the errors found here:
|
||||
/// https://github.com/jagerman/session-push-notification-server/blob/spns-v2/spns/hive/subscription.hpp#L21
|
||||
///
|
||||
/// Values at the time of writing are:
|
||||
/// OK = 0 // Great Success!
|
||||
/// BAD_INPUT = 1 // Unparseable, invalid values, missing required arguments, etc. (details in the string)
|
||||
/// SERVICE_NOT_AVAILABLE = 2 // The requested service name isn't currently available
|
||||
/// SERVICE_TIMEOUT = 3 // The backend service did not response
|
||||
/// ERROR = 4 // There was some other error processing the subscription (details in the string)
|
||||
/// INTERNAL_ERROR = 5 // An internal program error occured processing the request
|
||||
let error: Int?
|
||||
|
||||
/// Includes additional information about the error
|
||||
let message: String?
|
||||
}
|
||||
}
|
|
@ -3,136 +3,33 @@
|
|||
import Foundation
|
||||
import Combine
|
||||
import GRDB
|
||||
import Sodium
|
||||
import SessionSnodeKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public enum PushNotificationAPI {
|
||||
struct RegistrationRequestBody: Codable {
|
||||
let token: String
|
||||
let pubKey: String?
|
||||
}
|
||||
|
||||
struct NotifyRequestBody: Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case data
|
||||
case sendTo = "send_to"
|
||||
}
|
||||
|
||||
let data: String
|
||||
let sendTo: String
|
||||
}
|
||||
|
||||
struct ClosedGroupRequestBody: Codable {
|
||||
let closedGroupPublicKey: String
|
||||
let pubKey: String
|
||||
}
|
||||
|
||||
// MARK: - Settings
|
||||
|
||||
public static let server = "https://live.apns.getsession.org"
|
||||
public static let serverPublicKey = "642a6585919742e5a2d4dc51244964fbcd8bcab2b75612407de58b810740d049"
|
||||
|
||||
internal static let sodium: Atomic<Sodium> = Atomic(Sodium())
|
||||
private static let keychainService: String = "PNKeyChainService"
|
||||
private static let encryptionKeyKey: String = "PNEncryptionKeyKey"
|
||||
private static let encryptionKeyLength: Int = 32
|
||||
private static let maxRetryCount: Int = 4
|
||||
private static let tokenExpirationInterval: TimeInterval = 12 * 60 * 60
|
||||
|
||||
public enum ClosedGroupOperation: Int {
|
||||
case subscribe, unsubscribe
|
||||
|
||||
public var endpoint: String {
|
||||
switch self {
|
||||
case .subscribe: return "subscribe_closed_group"
|
||||
case .unsubscribe: return "unsubscribe_closed_group"
|
||||
}
|
||||
}
|
||||
}
|
||||
private static let tokenExpirationInterval: TimeInterval = (12 * 60 * 60)
|
||||
|
||||
// MARK: - Registration
|
||||
public static let server = "https://push.getsession.org"
|
||||
public static let serverPublicKey = "d7557fe563e2610de876c0ac7341b62f3c82d5eea4b62c702392ea4368f51b3b"
|
||||
public static let legacyServer = "https://live.apns.getsession.org"
|
||||
public static let legacyServerPublicKey = "642a6585919742e5a2d4dc51244964fbcd8bcab2b75612407de58b810740d049"
|
||||
|
||||
// MARK: - Requests
|
||||
|
||||
public static func unregister(_ token: Data) -> AnyPublisher<Void, Error> {
|
||||
let requestBody: RegistrationRequestBody = RegistrationRequestBody(token: token.toHexString(), pubKey: nil)
|
||||
|
||||
guard let body: Data = try? JSONEncoder().encode(requestBody) else {
|
||||
return Fail(error: HTTPError.invalidJSON)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
// Unsubscribe from all closed groups (including ones the user is no longer a member of,
|
||||
// just in case)
|
||||
Storage.shared
|
||||
.readPublisher { db -> (String, Set<String>) in
|
||||
(
|
||||
getUserHexEncodedPublicKey(db),
|
||||
try ClosedGroup
|
||||
.select(.threadId)
|
||||
.asRequest(of: String.self)
|
||||
.fetchSet(db)
|
||||
)
|
||||
}
|
||||
.flatMap { userPublicKey, closedGroupPublicKeys in
|
||||
Publishers
|
||||
.MergeMany(
|
||||
closedGroupPublicKeys
|
||||
.map { closedGroupPublicKey -> AnyPublisher<Void, Error> in
|
||||
PushNotificationAPI
|
||||
.performOperation(
|
||||
.unsubscribe,
|
||||
for: closedGroupPublicKey,
|
||||
publicKey: userPublicKey
|
||||
)
|
||||
}
|
||||
)
|
||||
.collect()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
.subscribe(on: DispatchQueue.global(qos: .userInitiated))
|
||||
.sinkUntilComplete()
|
||||
|
||||
// Unregister for normal push notifications
|
||||
let url = URL(string: "\(server)/unregister")!
|
||||
var request: URLRequest = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.allHTTPHeaderFields = [ HTTPHeader.contentType: "application/json" ]
|
||||
request.httpBody = body
|
||||
|
||||
return OnionRequestAPI
|
||||
.sendOnionRequest(request, to: server, with: serverPublicKey)
|
||||
.map { _, data -> Void in
|
||||
guard let response: PushServerResponse = try? data?.decoded(as: PushServerResponse.self) else {
|
||||
return SNLog("Couldn't unregister from push notifications.")
|
||||
}
|
||||
guard response.code != 0 else {
|
||||
return SNLog("Couldn't unregister from push notifications due to error: \(response.message ?? "nil").")
|
||||
}
|
||||
|
||||
return ()
|
||||
}
|
||||
.retry(maxRetryCount)
|
||||
.handleEvents(
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished: break
|
||||
case .failure: SNLog("Couldn't unregister from push notifications.")
|
||||
}
|
||||
}
|
||||
)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
public static func register(
|
||||
with token: Data,
|
||||
publicKey: String,
|
||||
isForcedUpdate: Bool
|
||||
public static func subscribe(
|
||||
token: Data,
|
||||
isForcedUpdate: Bool,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> AnyPublisher<Void, Error> {
|
||||
let hexEncodedToken: String = token.toHexString()
|
||||
let requestBody: RegistrationRequestBody = RegistrationRequestBody(token: hexEncodedToken, pubKey: publicKey)
|
||||
|
||||
guard let body: Data = try? JSONEncoder().encode(requestBody) else {
|
||||
return Fail(error: HTTPError.invalidJSON)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
let oldToken: String? = UserDefaults.standard[.deviceToken]
|
||||
let lastUploadTime: Double = UserDefaults.standard[.lastDeviceTokenUpload]
|
||||
let oldToken: String? = dependencies.standardUserDefaults[.deviceToken]
|
||||
let lastUploadTime: Double = dependencies.standardUserDefaults[.lastDeviceTokenUpload]
|
||||
let now: TimeInterval = Date().timeIntervalSince1970
|
||||
|
||||
guard isForcedUpdate || hexEncodedToken != oldToken || now - lastUploadTime > tokenExpirationInterval else {
|
||||
|
@ -142,153 +39,470 @@ public enum PushNotificationAPI {
|
|||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
let url = URL(string: "\(server)/register")!
|
||||
var request: URLRequest = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.allHTTPHeaderFields = [ HTTPHeader.contentType: "application/json" ]
|
||||
request.httpBody = body
|
||||
|
||||
return Publishers
|
||||
.MergeMany(
|
||||
[
|
||||
OnionRequestAPI
|
||||
.sendOnionRequest(request, to: server, with: serverPublicKey)
|
||||
.map { _, data -> Void in
|
||||
guard let response: PushServerResponse = try? data?.decoded(as: PushServerResponse.self) else {
|
||||
return SNLog("Couldn't register device token.")
|
||||
}
|
||||
guard response.code != 0 else {
|
||||
return SNLog("Couldn't register device token due to error: \(response.message ?? "nil").")
|
||||
}
|
||||
|
||||
UserDefaults.standard[.deviceToken] = hexEncodedToken
|
||||
UserDefaults.standard[.lastDeviceTokenUpload] = now
|
||||
UserDefaults.standard[.isUsingFullAPNs] = true
|
||||
return ()
|
||||
}
|
||||
.retry(maxRetryCount)
|
||||
.handleEvents(
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished: break
|
||||
case .failure: SNLog("Couldn't register device token.")
|
||||
}
|
||||
}
|
||||
)
|
||||
.eraseToAnyPublisher()
|
||||
].appending(
|
||||
contentsOf: Storage.shared
|
||||
.read { db -> [String] in
|
||||
try ClosedGroup
|
||||
.select(.threadId)
|
||||
.joining(
|
||||
required: ClosedGroup.members
|
||||
.filter(GroupMember.Columns.profileId == getUserHexEncodedPublicKey(db))
|
||||
)
|
||||
.asRequest(of: String.self)
|
||||
.fetchAll(db)
|
||||
}
|
||||
.defaulting(to: [])
|
||||
.map { closedGroupPublicKey -> AnyPublisher<Void, Error> in
|
||||
PushNotificationAPI
|
||||
.performOperation(
|
||||
.subscribe,
|
||||
for: closedGroupPublicKey,
|
||||
publicKey: publicKey
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
.collect()
|
||||
.map { _ in () }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
public static func performOperation(
|
||||
_ operation: ClosedGroupOperation,
|
||||
for closedGroupPublicKey: String,
|
||||
publicKey: String
|
||||
) -> AnyPublisher<Void, Error> {
|
||||
let isUsingFullAPNs = UserDefaults.standard[.isUsingFullAPNs]
|
||||
let requestBody: ClosedGroupRequestBody = ClosedGroupRequestBody(
|
||||
closedGroupPublicKey: closedGroupPublicKey,
|
||||
pubKey: publicKey
|
||||
)
|
||||
|
||||
guard isUsingFullAPNs else {
|
||||
guard let notificationsEncryptionKey: Data = try? getOrGenerateEncryptionKey(using: dependencies) else {
|
||||
SNLog("Unable to retrieve PN encryption key.")
|
||||
return Just(())
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
guard let body: Data = try? JSONEncoder().encode(requestBody) else {
|
||||
return Fail(error: HTTPError.invalidJSON)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
let url = URL(string: "\(server)/\(operation.endpoint)")!
|
||||
var request: URLRequest = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.allHTTPHeaderFields = [ HTTPHeader.contentType: "application/json" ]
|
||||
request.httpBody = body
|
||||
|
||||
return OnionRequestAPI
|
||||
.sendOnionRequest(request, to: server, with: serverPublicKey)
|
||||
.map { _, data in
|
||||
guard let response: PushServerResponse = try? data?.decoded(as: PushServerResponse.self) else {
|
||||
return SNLog("Couldn't subscribe/unsubscribe for closed group: \(closedGroupPublicKey).")
|
||||
}
|
||||
guard response.code != 0 else {
|
||||
return SNLog("Couldn't subscribe/unsubscribe for closed group: \(closedGroupPublicKey) due to error: \(response.message ?? "nil").")
|
||||
// TODO: Need to generate requests for each updated group as well
|
||||
return dependencies.storage
|
||||
.readPublisher(using: dependencies) { db -> (SubscribeRequest, String, Set<String>) in
|
||||
guard let userED25519KeyPair: KeyPair = Identity.fetchUserEd25519KeyPair(db) else {
|
||||
throw SnodeAPIError.noKeyPair
|
||||
}
|
||||
|
||||
return ()
|
||||
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
let request: SubscribeRequest = SubscribeRequest(
|
||||
pubkey: currentUserPublicKey,
|
||||
namespaces: [.default],
|
||||
// Note: Unfortunately we always need the message content because without the content
|
||||
// control messages can't be distinguished from visible messages which results in the
|
||||
// 'generic' notification being shown when receiving things like typing indicator updates
|
||||
includeMessageData: true,
|
||||
serviceInfo: SubscribeRequest.ServiceInfo(
|
||||
token: hexEncodedToken
|
||||
),
|
||||
notificationsEncryptionKey: notificationsEncryptionKey,
|
||||
subkey: nil,
|
||||
timestamp: (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000), // Seconds
|
||||
ed25519PublicKey: userED25519KeyPair.publicKey,
|
||||
ed25519SecretKey: userED25519KeyPair.secretKey
|
||||
)
|
||||
|
||||
return (
|
||||
request,
|
||||
currentUserPublicKey,
|
||||
try ClosedGroup
|
||||
.select(.threadId)
|
||||
.filter(!ClosedGroup.Columns.threadId.like("\(SessionId.Prefix.group.rawValue)%"))
|
||||
.joining(
|
||||
required: ClosedGroup.members
|
||||
.filter(GroupMember.Columns.profileId == currentUserPublicKey)
|
||||
)
|
||||
.asRequest(of: String.self)
|
||||
.fetchSet(db)
|
||||
)
|
||||
}
|
||||
.retry(maxRetryCount)
|
||||
.flatMap { request, currentUserPublicKey, legacyGroupIds -> AnyPublisher<Void, Error> in
|
||||
Publishers
|
||||
.MergeMany(
|
||||
[
|
||||
PushNotificationAPI
|
||||
.send(
|
||||
request: PushNotificationAPIRequest(
|
||||
endpoint: .subscribe,
|
||||
body: request
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
.decoded(as: SubscribeResponse.self, using: dependencies)
|
||||
.retry(maxRetryCount, using: dependencies)
|
||||
.handleEvents(
|
||||
receiveOutput: { _, response in
|
||||
guard response.success == true else {
|
||||
return SNLog("Couldn't subscribe for push notifications due to error (\(response.error ?? -1)): \(response.message ?? "nil").")
|
||||
}
|
||||
|
||||
dependencies.standardUserDefaults[.deviceToken] = hexEncodedToken
|
||||
dependencies.standardUserDefaults[.lastDeviceTokenUpload] = now
|
||||
dependencies.standardUserDefaults[.isUsingFullAPNs] = true
|
||||
},
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished: break
|
||||
case .failure: SNLog("Couldn't subscribe for push notifications.")
|
||||
}
|
||||
}
|
||||
)
|
||||
.map { _ in () }
|
||||
.eraseToAnyPublisher(),
|
||||
// FIXME: Remove this once legacy groups are deprecated
|
||||
PushNotificationAPI.subscribeToLegacyGroups(
|
||||
forced: true,
|
||||
token: hexEncodedToken,
|
||||
currentUserPublicKey: currentUserPublicKey,
|
||||
legacyGroupIds: legacyGroupIds,
|
||||
using: dependencies
|
||||
)
|
||||
]
|
||||
)
|
||||
.collect()
|
||||
.map { _ in () }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
public static func unsubscribe(
|
||||
token: Data,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> AnyPublisher<Void, Error> {
|
||||
let hexEncodedToken: String = token.toHexString()
|
||||
|
||||
// FIXME: Remove this once legacy groups are deprecated
|
||||
/// Unsubscribe from all legacy groups (including ones the user is no longer a member of, just in case)
|
||||
dependencies.storage
|
||||
.readPublisher(using: dependencies) { db -> (String, Set<String>) in
|
||||
(
|
||||
getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
try ClosedGroup
|
||||
.select(.threadId)
|
||||
.filter(!ClosedGroup.Columns.threadId.like("\(SessionId.Prefix.group.rawValue)%"))
|
||||
.asRequest(of: String.self)
|
||||
.fetchSet(db)
|
||||
)
|
||||
}
|
||||
.flatMap { currentUserPublicKey, legacyGroupIds in
|
||||
Publishers
|
||||
.MergeMany(
|
||||
legacyGroupIds
|
||||
.map { legacyGroupId -> AnyPublisher<Void, Error> in
|
||||
PushNotificationAPI
|
||||
.unsubscribeFromLegacyGroup(
|
||||
legacyGroupId: legacyGroupId,
|
||||
currentUserPublicKey: currentUserPublicKey,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
)
|
||||
.collect()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
.subscribe(on: DispatchQueue.global(qos: .userInitiated), using: dependencies)
|
||||
.sinkUntilComplete()
|
||||
|
||||
// TODO: Need to generate requests for each updated group as well
|
||||
return dependencies.storage
|
||||
.readPublisher(using: dependencies) { db -> UnsubscribeRequest in
|
||||
guard let userED25519KeyPair: KeyPair = Identity.fetchUserEd25519KeyPair(db) else {
|
||||
throw SnodeAPIError.noKeyPair
|
||||
}
|
||||
|
||||
return UnsubscribeRequest(
|
||||
pubkey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
serviceInfo: UnsubscribeRequest.ServiceInfo(
|
||||
token: hexEncodedToken
|
||||
),
|
||||
subkey: nil,
|
||||
timestamp: (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000), // Seconds
|
||||
ed25519PublicKey: userED25519KeyPair.publicKey,
|
||||
ed25519SecretKey: userED25519KeyPair.secretKey
|
||||
)
|
||||
}
|
||||
.flatMap { request -> AnyPublisher<Void, Error> in
|
||||
PushNotificationAPI
|
||||
.send(
|
||||
request: PushNotificationAPIRequest(
|
||||
endpoint: .unsubscribe,
|
||||
body: request
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
.decoded(as: UnsubscribeResponse.self, using: dependencies)
|
||||
.retry(maxRetryCount, using: dependencies)
|
||||
.handleEvents(
|
||||
receiveOutput: { _, response in
|
||||
guard response.success == true else {
|
||||
return SNLog("Couldn't unsubscribe for push notifications due to error (\(response.error ?? -1)): \(response.message ?? "nil").")
|
||||
}
|
||||
|
||||
dependencies.standardUserDefaults[.deviceToken] = nil
|
||||
},
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished: break
|
||||
case .failure: SNLog("Couldn't unsubscribe for push notifications.")
|
||||
}
|
||||
}
|
||||
)
|
||||
.map { _ in () }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
// MARK: - Legacy Notifications
|
||||
|
||||
// FIXME: Remove this once legacy notifications and legacy groups are deprecated
|
||||
public static func legacyNotify(
|
||||
recipient: String,
|
||||
with message: String,
|
||||
maxRetryCount: Int? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> AnyPublisher<Void, Error> {
|
||||
return PushNotificationAPI
|
||||
.send(
|
||||
request: PushNotificationAPIRequest(
|
||||
endpoint: .legacyNotify,
|
||||
body: LegacyNotifyRequest(
|
||||
data: message,
|
||||
sendTo: recipient
|
||||
)
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
.decoded(as: LegacyPushServerResponse.self, using: dependencies)
|
||||
.retry(maxRetryCount ?? PushNotificationAPI.maxRetryCount, using: dependencies)
|
||||
.handleEvents(
|
||||
receiveOutput: { _, response in
|
||||
guard response.code != 0 else {
|
||||
return SNLog("Couldn't send push notification due to error: \(response.message ?? "nil").")
|
||||
}
|
||||
},
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished: break
|
||||
case .failure:
|
||||
SNLog("Couldn't subscribe/unsubscribe for closed group: \(closedGroupPublicKey).")
|
||||
case .failure: SNLog("Couldn't send push notification.")
|
||||
}
|
||||
}
|
||||
)
|
||||
.map { _ in () }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
// MARK: - Notify
|
||||
// MARK: - Legacy Groups
|
||||
|
||||
public static func notify(
|
||||
recipient: String,
|
||||
with message: String,
|
||||
maxRetryCount: Int? = nil
|
||||
// FIXME: Remove this once legacy groups are deprecated
|
||||
public static func subscribeToLegacyGroups(
|
||||
forced: Bool = false,
|
||||
token: String? = nil,
|
||||
currentUserPublicKey: String,
|
||||
legacyGroupIds: Set<String>,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> AnyPublisher<Void, Error> {
|
||||
let requestBody: NotifyRequestBody = NotifyRequestBody(data: message, sendTo: recipient)
|
||||
let isUsingFullAPNs = dependencies.standardUserDefaults[.isUsingFullAPNs]
|
||||
|
||||
guard let body: Data = try? JSONEncoder().encode(requestBody) else {
|
||||
// Only continue if PNs are enabled and we have a device token
|
||||
guard
|
||||
(forced || isUsingFullAPNs),
|
||||
let deviceToken: String = (token ?? dependencies.standardUserDefaults[.deviceToken])
|
||||
else {
|
||||
return Just(())
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
return PushNotificationAPI
|
||||
.send(
|
||||
request: PushNotificationAPIRequest(
|
||||
endpoint: .legacyGroupsOnlySubscribe,
|
||||
body: LegacyGroupOnlyRequest(
|
||||
token: deviceToken,
|
||||
pubKey: currentUserPublicKey,
|
||||
device: "ios",
|
||||
legacyGroupPublicKeys: legacyGroupIds
|
||||
)
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
.decoded(as: LegacyPushServerResponse.self, using: dependencies)
|
||||
.retry(maxRetryCount, using: dependencies)
|
||||
.handleEvents(
|
||||
receiveOutput: { _, response in
|
||||
guard response.code != 0 else {
|
||||
return SNLog("Couldn't subscribe for legacy groups due to error: \(response.message ?? "nil").")
|
||||
}
|
||||
},
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished: break
|
||||
case .failure: SNLog("Couldn't subscribe for legacy groups.")
|
||||
}
|
||||
}
|
||||
)
|
||||
.map { _ in () }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
// FIXME: Remove this once legacy groups are deprecated
|
||||
public static func unsubscribeFromLegacyGroup(
|
||||
legacyGroupId: String,
|
||||
currentUserPublicKey: String,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> AnyPublisher<Void, Error> {
|
||||
return PushNotificationAPI
|
||||
.send(
|
||||
request: PushNotificationAPIRequest(
|
||||
endpoint: .legacyGroupUnsubscribe,
|
||||
body: LegacyGroupRequest(
|
||||
pubKey: currentUserPublicKey,
|
||||
closedGroupPublicKey: legacyGroupId
|
||||
)
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
.decoded(as: LegacyPushServerResponse.self, using: dependencies)
|
||||
.retry(maxRetryCount, using: dependencies)
|
||||
.handleEvents(
|
||||
receiveOutput: { _, response in
|
||||
guard response.code != 0 else {
|
||||
return SNLog("Couldn't unsubscribe for legacy group: \(legacyGroupId) due to error: \(response.message ?? "nil").")
|
||||
}
|
||||
},
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished: break
|
||||
case .failure: SNLog("Couldn't unsubscribe for legacy group: \(legacyGroupId).")
|
||||
}
|
||||
}
|
||||
)
|
||||
.map { _ in () }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
// MARK: - Notification Handling
|
||||
|
||||
public static func processNotification(
|
||||
notificationContent: UNNotificationContent,
|
||||
dependencies: Dependencies = Dependencies()
|
||||
) -> (envelope: SNProtoEnvelope?, result: ProcessResult) {
|
||||
// Make sure the notification is from the updated push server
|
||||
guard notificationContent.userInfo["spns"] != nil else {
|
||||
guard
|
||||
let base64EncodedData: String = notificationContent.userInfo["ENCRYPTED_DATA"] as? String,
|
||||
let data: Data = Data(base64Encoded: base64EncodedData),
|
||||
let envelope: SNProtoEnvelope = try? MessageWrapper.unwrap(data: data)
|
||||
else { return (nil, .legacyFailure) }
|
||||
|
||||
// We only support legacy notifications for legacy group conversations
|
||||
guard envelope.type == .closedGroupMessage else { return (envelope, .legacyForceSilent) }
|
||||
|
||||
return (envelope, .legacySuccess)
|
||||
}
|
||||
|
||||
guard
|
||||
let base64EncodedEncString: String = notificationContent.userInfo["enc_payload"] as? String,
|
||||
let encData: Data = Data(base64Encoded: base64EncodedEncString),
|
||||
let notificationsEncryptionKey: Data = try? getOrGenerateEncryptionKey(using: dependencies),
|
||||
encData.count > dependencies.crypto.size(.aeadXChaCha20NonceBytes)
|
||||
else { return (nil, .failure) }
|
||||
|
||||
let nonce: Data = encData[0..<dependencies.crypto.size(.aeadXChaCha20NonceBytes)]
|
||||
let payload: Data = encData[dependencies.crypto.size(.aeadXChaCha20NonceBytes)...]
|
||||
|
||||
guard
|
||||
let paddedData: [UInt8] = try? dependencies.crypto.perform(
|
||||
.decryptAeadXChaCha20(
|
||||
authenticatedCipherText: payload.bytes,
|
||||
secretKey: notificationsEncryptionKey.bytes,
|
||||
nonce: nonce.bytes
|
||||
)
|
||||
)
|
||||
else { return (nil, .failure) }
|
||||
|
||||
let decryptedData: Data = Data(paddedData.reversed().drop(while: { $0 == 0 }).reversed())
|
||||
|
||||
// Decode the decrypted data
|
||||
guard let notification: BencodeResponse<NotificationMetadata> = try? Bencode.decodeResponse(from: decryptedData) else {
|
||||
return (nil, .failure)
|
||||
}
|
||||
|
||||
// If the metadata says that the message was too large then we should show the generic
|
||||
// notification (this is a valid case)
|
||||
guard !notification.info.dataTooLong else { return (nil, .success) }
|
||||
|
||||
// Check that the body we were given is valid
|
||||
guard
|
||||
let notificationData: Data = notification.data,
|
||||
notification.info.dataLength == notificationData.count,
|
||||
let envelope = try? MessageWrapper.unwrap(data: notificationData)
|
||||
else { return (nil, .failure) }
|
||||
|
||||
// Success, we have the notification content
|
||||
return (envelope, .success)
|
||||
}
|
||||
|
||||
// MARK: - Security
|
||||
|
||||
@discardableResult private static func getOrGenerateEncryptionKey(using dependencies: Dependencies) throws -> Data {
|
||||
do {
|
||||
var encryptionKey: Data = try SSKDefaultKeychainStorage.shared.data(
|
||||
forService: keychainService,
|
||||
key: encryptionKeyKey
|
||||
)
|
||||
defer { encryptionKey.resetBytes(in: 0..<encryptionKey.count) }
|
||||
|
||||
guard encryptionKey.count == encryptionKeyLength else { throw StorageError.invalidKeySpec }
|
||||
|
||||
return encryptionKey
|
||||
}
|
||||
catch {
|
||||
switch (error, (error as? KeychainStorageError)?.code) {
|
||||
case (StorageError.invalidKeySpec, _), (_, errSecItemNotFound):
|
||||
// No keySpec was found so we need to generate a new one
|
||||
do {
|
||||
var keySpec: Data = try Randomness.generateRandomBytes(numberBytes: encryptionKeyLength)
|
||||
defer { keySpec.resetBytes(in: 0..<keySpec.count) } // Reset content immediately after use
|
||||
|
||||
try SSKDefaultKeychainStorage.shared.set(
|
||||
data: keySpec,
|
||||
service: keychainService,
|
||||
key: encryptionKeyKey
|
||||
)
|
||||
return keySpec
|
||||
}
|
||||
catch {
|
||||
SNLog("Setting keychain value failed with error: \(error.localizedDescription)")
|
||||
throw StorageError.keySpecCreationFailed
|
||||
}
|
||||
|
||||
default:
|
||||
// Because we use kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, the keychain will be inaccessible
|
||||
// after device restart until device is unlocked for the first time. If the app receives a push
|
||||
// notification, we won't be able to access the keychain to process that notification, so we should
|
||||
// just terminate by throwing an uncaught exception
|
||||
if CurrentAppContext().isMainApp || CurrentAppContext().isInBackground() {
|
||||
let appState: UIApplication.State = CurrentAppContext().reportedApplicationState
|
||||
SNLog("CipherKeySpec inaccessible. New install or no unlock since device restart?, ApplicationState: \(NSStringForUIApplicationState(appState))")
|
||||
throw StorageError.keySpecInaccessible
|
||||
}
|
||||
|
||||
SNLog("CipherKeySpec inaccessible; not main app.")
|
||||
throw StorageError.keySpecInaccessible
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Convenience
|
||||
|
||||
private static func send<T: Encodable>(
|
||||
request: PushNotificationAPIRequest<T>,
|
||||
using dependencies: Dependencies
|
||||
) -> AnyPublisher<(ResponseInfoType, Data?), Error> {
|
||||
guard
|
||||
let url: URL = URL(string: "\(request.endpoint.server)/\(request.endpoint.rawValue)"),
|
||||
let payload: Data = try? JSONEncoder().encode(request.body)
|
||||
else {
|
||||
return Fail(error: HTTPError.invalidJSON)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
let url = URL(string: "\(server)/notify")!
|
||||
var request: URLRequest = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.allHTTPHeaderFields = [ HTTPHeader.contentType: "application/json" ]
|
||||
request.httpBody = body
|
||||
guard Features.useOnionRequests else {
|
||||
return HTTP
|
||||
.execute(
|
||||
.post,
|
||||
"\(request.endpoint.server)/\(request.endpoint.rawValue)",
|
||||
body: payload
|
||||
)
|
||||
.map { response in (HTTP.ResponseInfo(code: -1, headers: [:]), response) }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
return OnionRequestAPI
|
||||
.sendOnionRequest(request, to: server, with: serverPublicKey)
|
||||
.map { _, data -> Void in
|
||||
guard let response: PushServerResponse = try? data?.decoded(as: PushServerResponse.self) else {
|
||||
return SNLog("Couldn't send push notification.")
|
||||
}
|
||||
guard response.code != 0 else {
|
||||
return SNLog("Couldn't send push notification due to error: \(response.message ?? "nil").")
|
||||
}
|
||||
|
||||
return ()
|
||||
}
|
||||
.retry(maxRetryCount ?? PushNotificationAPI.maxRetryCount)
|
||||
var urlRequest: URLRequest = URLRequest(url: url)
|
||||
urlRequest.httpMethod = "POST"
|
||||
urlRequest.allHTTPHeaderFields = [ HTTPHeader.contentType: "application/json" ]
|
||||
urlRequest.httpBody = payload
|
||||
|
||||
return dependencies.network
|
||||
.send(
|
||||
.onionRequest(
|
||||
urlRequest,
|
||||
to: request.endpoint.server,
|
||||
with: request.endpoint.serverPublicKey
|
||||
)
|
||||
)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension PushNotificationAPI {
|
||||
enum ProcessResult {
|
||||
case success
|
||||
case failure
|
||||
case legacySuccess
|
||||
case legacyFailure
|
||||
case legacyForceSilent
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue