Fixed build errors and cleaned up Dependencies interface further
Fixed a build error Cleaned up the Dependencies interface for UserDefaults and Storage settings Refactored the AppVersion class to be in Swift
This commit is contained in:
parent
7b04a4b888
commit
3abeeffd3d
|
@ -280,7 +280,7 @@
|
||||||
C328255225CA64470062D0A7 /* ContextMenuVC+ActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328255125CA64470062D0A7 /* ContextMenuVC+ActionView.swift */; };
|
C328255225CA64470062D0A7 /* ContextMenuVC+ActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328255125CA64470062D0A7 /* ContextMenuVC+ActionView.swift */; };
|
||||||
C32C598A256D0664003C73A2 /* SNProtoEnvelope+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */; };
|
C32C598A256D0664003C73A2 /* SNProtoEnvelope+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */; };
|
||||||
C32C599E256DB02B003C73A2 /* TypingIndicators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */; };
|
C32C599E256DB02B003C73A2 /* TypingIndicators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */; };
|
||||||
C32C5A24256DB7DB003C73A2 /* SNUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6B255A580F00E217F9 /* SNUserDefaults.swift */; };
|
C32C5A24256DB7DB003C73A2 /* UserDefaultsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6B255A580F00E217F9 /* UserDefaultsInfo.swift */; };
|
||||||
C32C5A48256DB8F0003C73A2 /* BuildConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */; };
|
C32C5A48256DB8F0003C73A2 /* BuildConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */; };
|
||||||
C32C5A88256DBCF9003C73A2 /* MessageReceiver+LegacyClosedGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = C32C5A87256DBCF9003C73A2 /* MessageReceiver+LegacyClosedGroups.swift */; };
|
C32C5A88256DBCF9003C73A2 /* MessageReceiver+LegacyClosedGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = C32C5A87256DBCF9003C73A2 /* MessageReceiver+LegacyClosedGroups.swift */; };
|
||||||
C32C5C3D256DCBAF003C73A2 /* AppReadiness.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB75255A581000E217F9 /* AppReadiness.m */; };
|
C32C5C3D256DCBAF003C73A2 /* AppReadiness.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB75255A581000E217F9 /* AppReadiness.m */; };
|
||||||
|
@ -291,8 +291,6 @@
|
||||||
C32C5DD2256DD9E5003C73A2 /* LRUCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAFD255A580600E217F9 /* LRUCache.swift */; };
|
C32C5DD2256DD9E5003C73A2 /* LRUCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAFD255A580600E217F9 /* LRUCache.swift */; };
|
||||||
C32C5DDB256DD9FF003C73A2 /* ContentProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB68255A580F00E217F9 /* ContentProxy.swift */; };
|
C32C5DDB256DD9FF003C73A2 /* ContentProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB68255A580F00E217F9 /* ContentProxy.swift */; };
|
||||||
C32C5E0C256DDAFA003C73A2 /* NSRegularExpression+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */; };
|
C32C5E0C256DDAFA003C73A2 /* NSRegularExpression+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */; };
|
||||||
C32C600F256E07F5003C73A2 /* NSUserDefaults+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB77255A581000E217F9 /* NSUserDefaults+OWS.m */; };
|
|
||||||
C32C6018256E07F9003C73A2 /* NSUserDefaults+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB51255A580D00E217F9 /* NSUserDefaults+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
||||||
C33100282559000A00070591 /* UIView+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33100272559000A00070591 /* UIView+Utilities.swift */; };
|
C33100282559000A00070591 /* UIView+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33100272559000A00070591 /* UIView+Utilities.swift */; };
|
||||||
C331FF1F2558F9D300070591 /* SessionUIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C331FF1D2558F9D300070591 /* SessionUIKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
C331FF1F2558F9D300070591 /* SessionUIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C331FF1D2558F9D300070591 /* SessionUIKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
C331FF222558F9D300070591 /* SessionUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C331FF1B2558F9D300070591 /* SessionUIKit.framework */; };
|
C331FF222558F9D300070591 /* SessionUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C331FF1B2558F9D300070591 /* SessionUIKit.framework */; };
|
||||||
|
@ -316,11 +314,9 @@
|
||||||
C33FD9C4255A54EF00E217F9 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; };
|
C33FD9C4255A54EF00E217F9 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; };
|
||||||
C33FD9C5255A54EF00E217F9 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; };
|
C33FD9C5255A54EF00E217F9 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; };
|
||||||
C33FDC29255A581F00E217F9 /* ReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */; };
|
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 */; };
|
C33FDC58255A582000E217F9 /* ReverseDispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA9E255A57FF00E217F9 /* ReverseDispatchQueue.swift */; };
|
||||||
C33FDC98255A582000E217F9 /* SwiftSingletons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDADE255A580400E217F9 /* SwiftSingletons.swift */; };
|
C33FDC98255A582000E217F9 /* SwiftSingletons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDADE255A580400E217F9 /* SwiftSingletons.swift */; };
|
||||||
C33FDD03255A582000E217F9 /* WeakTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB49255A580C00E217F9 /* WeakTimer.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 */; };
|
C33FDD23255A582000E217F9 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB69255A580F00E217F9 /* FeatureFlags.swift */; };
|
||||||
C33FDD3A255A582000E217F9 /* Notification+Loki.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB80255A581100E217F9 /* Notification+Loki.swift */; };
|
C33FDD3A255A582000E217F9 /* Notification+Loki.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB80255A581100E217F9 /* Notification+Loki.swift */; };
|
||||||
C33FDD49255A582000E217F9 /* ParamParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB8F255A581200E217F9 /* ParamParser.swift */; };
|
C33FDD49255A582000E217F9 /* ParamParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB8F255A581200E217F9 /* ParamParser.swift */; };
|
||||||
|
@ -495,6 +491,7 @@
|
||||||
FD09C5EC282B8F18000CE219 /* AttachmentError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD09C5EB282B8F17000CE219 /* AttachmentError.swift */; };
|
FD09C5EC282B8F18000CE219 /* AttachmentError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD09C5EB282B8F17000CE219 /* AttachmentError.swift */; };
|
||||||
FD0B77B029B69A65009169BA /* TopBannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0B77AF29B69A65009169BA /* TopBannerController.swift */; };
|
FD0B77B029B69A65009169BA /* TopBannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0B77AF29B69A65009169BA /* TopBannerController.swift */; };
|
||||||
FD0B77B229B82B7A009169BA /* ArrayUtilitiesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0B77B129B82B7A009169BA /* ArrayUtilitiesSpec.swift */; };
|
FD0B77B229B82B7A009169BA /* ArrayUtilitiesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0B77B129B82B7A009169BA /* ArrayUtilitiesSpec.swift */; };
|
||||||
|
FD0E353C2AB9880B006A81F7 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0E353A2AB98773006A81F7 /* AppVersion.swift */; };
|
||||||
FD16AB5B2A1DD7CA0083D849 /* PlaceholderIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2A3255B6D93007E1867 /* PlaceholderIcon.swift */; };
|
FD16AB5B2A1DD7CA0083D849 /* PlaceholderIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2A3255B6D93007E1867 /* PlaceholderIcon.swift */; };
|
||||||
FD16AB5F2A1DD98F0083D849 /* ProfilePictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2A4255B6D93007E1867 /* ProfilePictureView.swift */; };
|
FD16AB5F2A1DD98F0083D849 /* ProfilePictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2A4255B6D93007E1867 /* ProfilePictureView.swift */; };
|
||||||
FD16AB612A1DD9B60083D849 /* ProfilePictureView+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD16AB602A1DD9B60083D849 /* ProfilePictureView+Convenience.swift */; };
|
FD16AB612A1DD9B60083D849 /* ProfilePictureView+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD16AB602A1DD9B60083D849 /* ProfilePictureView+Convenience.swift */; };
|
||||||
|
@ -1502,7 +1499,6 @@
|
||||||
C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityManager.swift; sourceTree = "<group>"; };
|
C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityManager.swift; sourceTree = "<group>"; };
|
||||||
C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+SSK.swift"; sourceTree = "<group>"; };
|
C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+SSK.swift"; sourceTree = "<group>"; };
|
||||||
C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicators.swift; sourceTree = "<group>"; };
|
C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicators.swift; sourceTree = "<group>"; };
|
||||||
C33FDA8B255A57FD00E217F9 /* AppVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppVersion.m; sourceTree = "<group>"; };
|
|
||||||
C33FDA8E255A57FD00E217F9 /* OWSFileSystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSFileSystem.m; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BuildConfiguration.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1523,14 +1519,11 @@
|
||||||
C33FDB3F255A580C00E217F9 /* String+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+SSK.swift"; sourceTree = "<group>"; };
|
C33FDB3F255A580C00E217F9 /* String+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+SSK.swift"; sourceTree = "<group>"; };
|
||||||
C33FDB41255A580C00E217F9 /* MIMETypeUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIMETypeUtil.m; 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>"; };
|
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>"; };
|
|
||||||
C33FDB51255A580D00E217F9 /* NSUserDefaults+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+OWS.h"; sourceTree = "<group>"; };
|
|
||||||
C33FDB54255A580D00E217F9 /* DataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataSource.h; sourceTree = "<group>"; };
|
C33FDB54255A580D00E217F9 /* DataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataSource.h; sourceTree = "<group>"; };
|
||||||
C33FDB68255A580F00E217F9 /* ContentProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentProxy.swift; sourceTree = "<group>"; };
|
C33FDB68255A580F00E217F9 /* ContentProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentProxy.swift; sourceTree = "<group>"; };
|
||||||
C33FDB69255A580F00E217F9 /* FeatureFlags.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = "<group>"; };
|
C33FDB69255A580F00E217F9 /* FeatureFlags.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = "<group>"; };
|
||||||
C33FDB6B255A580F00E217F9 /* SNUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SNUserDefaults.swift; sourceTree = "<group>"; };
|
C33FDB6B255A580F00E217F9 /* UserDefaultsInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsInfo.swift; sourceTree = "<group>"; };
|
||||||
C33FDB75255A581000E217F9 /* AppReadiness.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppReadiness.m; 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>"; };
|
|
||||||
C33FDB80255A581100E217F9 /* Notification+Loki.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Notification+Loki.swift"; 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>"; };
|
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>"; };
|
C33FDB85255A581100E217F9 /* AppContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppContext.m; sourceTree = "<group>"; };
|
||||||
|
@ -1737,6 +1730,7 @@
|
||||||
FD09C5EB282B8F17000CE219 /* AttachmentError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentError.swift; sourceTree = "<group>"; };
|
FD09C5EB282B8F17000CE219 /* AttachmentError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentError.swift; sourceTree = "<group>"; };
|
||||||
FD0B77AF29B69A65009169BA /* TopBannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBannerController.swift; sourceTree = "<group>"; };
|
FD0B77AF29B69A65009169BA /* TopBannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBannerController.swift; sourceTree = "<group>"; };
|
||||||
FD0B77B129B82B7A009169BA /* ArrayUtilitiesSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayUtilitiesSpec.swift; sourceTree = "<group>"; };
|
FD0B77B129B82B7A009169BA /* ArrayUtilitiesSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayUtilitiesSpec.swift; sourceTree = "<group>"; };
|
||||||
|
FD0E353A2AB98773006A81F7 /* AppVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersion.swift; sourceTree = "<group>"; };
|
||||||
FD16AB602A1DD9B60083D849 /* ProfilePictureView+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfilePictureView+Convenience.swift"; sourceTree = "<group>"; };
|
FD16AB602A1DD9B60083D849 /* ProfilePictureView+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfilePictureView+Convenience.swift"; sourceTree = "<group>"; };
|
||||||
FD17D79527F3E04600122BE0 /* _001_InitialSetupMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _001_InitialSetupMigration.swift; sourceTree = "<group>"; };
|
FD17D79527F3E04600122BE0 /* _001_InitialSetupMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _001_InitialSetupMigration.swift; sourceTree = "<group>"; };
|
||||||
FD17D79827F40AB800122BE0 /* _003_YDBToGRDBMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _003_YDBToGRDBMigration.swift; sourceTree = "<group>"; };
|
FD17D79827F40AB800122BE0 /* _003_YDBToGRDBMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _003_YDBToGRDBMigration.swift; sourceTree = "<group>"; };
|
||||||
|
@ -2776,12 +2770,9 @@
|
||||||
C352A3762557859C00338F3E /* NSTimer+Proxying.h */,
|
C352A3762557859C00338F3E /* NSTimer+Proxying.h */,
|
||||||
C352A36C2557858D00338F3E /* NSTimer+Proxying.m */,
|
C352A36C2557858D00338F3E /* NSTimer+Proxying.m */,
|
||||||
7B1D74AF27C365960030B423 /* Timer+MainThread.swift */,
|
7B1D74AF27C365960030B423 /* Timer+MainThread.swift */,
|
||||||
C33FDB51255A580D00E217F9 /* NSUserDefaults+OWS.h */,
|
|
||||||
C33FDB77255A581000E217F9 /* NSUserDefaults+OWS.m */,
|
|
||||||
C33FDB14255A580800E217F9 /* OWSMath.h */,
|
C33FDB14255A580800E217F9 /* OWSMath.h */,
|
||||||
FD705A91278D051200F16121 /* ReusableView.swift */,
|
FD705A91278D051200F16121 /* ReusableView.swift */,
|
||||||
FD17D7AF27F4225C00122BE0 /* Set+Utilities.swift */,
|
FD17D7AF27F4225C00122BE0 /* Set+Utilities.swift */,
|
||||||
C33FDB6B255A580F00E217F9 /* SNUserDefaults.swift */,
|
|
||||||
FD77289D284EF1C50018502F /* Sodium+Utilities.swift */,
|
FD77289D284EF1C50018502F /* Sodium+Utilities.swift */,
|
||||||
C33FDB3F255A580C00E217F9 /* String+SSK.swift */,
|
C33FDB3F255A580C00E217F9 /* String+SSK.swift */,
|
||||||
C3C2AC2D2553CBEB00C340D1 /* String+Trimming.swift */,
|
C3C2AC2D2553CBEB00C340D1 /* String+Trimming.swift */,
|
||||||
|
@ -3543,8 +3534,6 @@
|
||||||
C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */,
|
C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */,
|
||||||
C33FDBD3255A581800E217F9 /* OWSSignalAddress.swift */,
|
C33FDBD3255A581800E217F9 /* OWSSignalAddress.swift */,
|
||||||
C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */,
|
C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */,
|
||||||
C33FDB4C255A580D00E217F9 /* AppVersion.h */,
|
|
||||||
C33FDA8B255A57FD00E217F9 /* AppVersion.m */,
|
|
||||||
C38EF3E4255B6DF4007E1867 /* CommonStrings.swift */,
|
C38EF3E4255B6DF4007E1867 /* CommonStrings.swift */,
|
||||||
C38EF304255B6DBE007E1867 /* ImageCache.swift */,
|
C38EF304255B6DBE007E1867 /* ImageCache.swift */,
|
||||||
C38EF2F2255B6DBC007E1867 /* Searcher.swift */,
|
C38EF2F2255B6DBC007E1867 /* Searcher.swift */,
|
||||||
|
@ -3714,6 +3703,7 @@
|
||||||
FD09796527F6B0A800936362 /* Utilities */ = {
|
FD09796527F6B0A800936362 /* Utilities */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
FD0E353A2AB98773006A81F7 /* AppVersion.swift */,
|
||||||
FDFBB74A2A1EFF4900CA7350 /* Bencode.swift */,
|
FDFBB74A2A1EFF4900CA7350 /* Bencode.swift */,
|
||||||
FD97B23F2A3FEB050027DD57 /* ARC4RandomNumberGenerator.swift */,
|
FD97B23F2A3FEB050027DD57 /* ARC4RandomNumberGenerator.swift */,
|
||||||
FDA8EB0F280F8238002B68E5 /* Codable+Utilities.swift */,
|
FDA8EB0F280F8238002B68E5 /* Codable+Utilities.swift */,
|
||||||
|
@ -4531,6 +4521,7 @@
|
||||||
FD23CE2F2A67B8820000B97C /* CacheInfo.swift */,
|
FD23CE2F2A67B8820000B97C /* CacheInfo.swift */,
|
||||||
FDC6D75F2862B3F600B04575 /* Dependencies.swift */,
|
FDC6D75F2862B3F600B04575 /* Dependencies.swift */,
|
||||||
FDF01FAC2A9ECC4200CAF969 /* SingletonInfo.swift */,
|
FDF01FAC2A9ECC4200CAF969 /* SingletonInfo.swift */,
|
||||||
|
C33FDB6B255A580F00E217F9 /* UserDefaultsInfo.swift */,
|
||||||
);
|
);
|
||||||
path = "Dependency Injection";
|
path = "Dependency Injection";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -4687,7 +4678,6 @@
|
||||||
files = (
|
files = (
|
||||||
C38EF35E255B6DCC007E1867 /* OWSViewController.h in Headers */,
|
C38EF35E255B6DCC007E1867 /* OWSViewController.h in Headers */,
|
||||||
C33FD9AF255A548A00E217F9 /* SignalUtilitiesKit.h in Headers */,
|
C33FD9AF255A548A00E217F9 /* SignalUtilitiesKit.h in Headers */,
|
||||||
C33FDD06255A582000E217F9 /* AppVersion.h in Headers */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -4713,7 +4703,6 @@
|
||||||
C352A3772557864000338F3E /* NSTimer+Proxying.h in Headers */,
|
C352A3772557864000338F3E /* NSTimer+Proxying.h in Headers */,
|
||||||
FD30036A2A3ADEC100B5A5FB /* CExceptionHelper.h in Headers */,
|
FD30036A2A3ADEC100B5A5FB /* CExceptionHelper.h in Headers */,
|
||||||
C3C2A67D255388CC00C340D1 /* SessionUtilitiesKit.h in Headers */,
|
C3C2A67D255388CC00C340D1 /* SessionUtilitiesKit.h in Headers */,
|
||||||
C32C6018256E07F9003C73A2 /* NSUserDefaults+OWS.h in Headers */,
|
|
||||||
B8856D8D256F1502001CE70E /* UIView+OWS.h in Headers */,
|
B8856D8D256F1502001CE70E /* UIView+OWS.h in Headers */,
|
||||||
FD52090128AF61BA006098F6 /* OWSBackgroundTask.h in Headers */,
|
FD52090128AF61BA006098F6 /* OWSBackgroundTask.h in Headers */,
|
||||||
);
|
);
|
||||||
|
@ -5828,7 +5817,6 @@
|
||||||
C33FDC29255A581F00E217F9 /* ReachabilityManager.swift in Sources */,
|
C33FDC29255A581F00E217F9 /* ReachabilityManager.swift in Sources */,
|
||||||
C38EF407255B6DF7007E1867 /* Toast.swift in Sources */,
|
C38EF407255B6DF7007E1867 /* Toast.swift in Sources */,
|
||||||
C38EF38C255B6DD2007E1867 /* ApprovalRailCellView.swift in Sources */,
|
C38EF38C255B6DD2007E1867 /* ApprovalRailCellView.swift in Sources */,
|
||||||
C33FDC45255A581F00E217F9 /* AppVersion.m in Sources */,
|
|
||||||
C38EF3C7255B6DE7007E1867 /* ImageEditorCanvasView.swift in Sources */,
|
C38EF3C7255B6DE7007E1867 /* ImageEditorCanvasView.swift in Sources */,
|
||||||
C38EF400255B6DF7007E1867 /* GalleryRailView.swift in Sources */,
|
C38EF400255B6DF7007E1867 /* GalleryRailView.swift in Sources */,
|
||||||
C38EF32E255B6DBF007E1867 /* ImageCache.swift in Sources */,
|
C38EF32E255B6DBF007E1867 /* ImageCache.swift in Sources */,
|
||||||
|
@ -5973,6 +5961,7 @@
|
||||||
FD7115FE28C8202D00B47552 /* ReplaySubject.swift in Sources */,
|
FD7115FE28C8202D00B47552 /* ReplaySubject.swift in Sources */,
|
||||||
C32C5DC9256DD935003C73A2 /* ProxiedContentDownloader.swift in Sources */,
|
C32C5DC9256DD935003C73A2 /* ProxiedContentDownloader.swift in Sources */,
|
||||||
C3D9E4C02567767F0040E4F3 /* DataSource.m in Sources */,
|
C3D9E4C02567767F0040E4F3 /* DataSource.m in Sources */,
|
||||||
|
FD0E353C2AB9880B006A81F7 /* AppVersion.swift in Sources */,
|
||||||
C32C5DD2256DD9E5003C73A2 /* LRUCache.swift in Sources */,
|
C32C5DD2256DD9E5003C73A2 /* LRUCache.swift in Sources */,
|
||||||
FD71160228C8255900B47552 /* UIControl+Combine.swift in Sources */,
|
FD71160228C8255900B47552 /* UIControl+Combine.swift in Sources */,
|
||||||
FDFBB74B2A1EFF4900CA7350 /* Bencode.swift in Sources */,
|
FDFBB74B2A1EFF4900CA7350 /* Bencode.swift in Sources */,
|
||||||
|
@ -5988,7 +5977,6 @@
|
||||||
FD30036E2A3AE26000B5A5FB /* CExceptionHelper.mm in Sources */,
|
FD30036E2A3AE26000B5A5FB /* CExceptionHelper.mm in Sources */,
|
||||||
FD9AECA72AAAF5B0009B3406 /* Crypto+SessionUtilitiesKit.swift in Sources */,
|
FD9AECA72AAAF5B0009B3406 /* Crypto+SessionUtilitiesKit.swift in Sources */,
|
||||||
C3D9E4DA256778410040E4F3 /* UIImage+OWS.m in Sources */,
|
C3D9E4DA256778410040E4F3 /* UIImage+OWS.m in Sources */,
|
||||||
C32C600F256E07F5003C73A2 /* NSUserDefaults+OWS.m in Sources */,
|
|
||||||
FDE658A329418E2F00A33BC1 /* KeyPair.swift in Sources */,
|
FDE658A329418E2F00A33BC1 /* KeyPair.swift in Sources */,
|
||||||
FD5931AB2A8DCB0A0040147D /* ScopeAdapter+Utilities.swift in Sources */,
|
FD5931AB2A8DCB0A0040147D /* ScopeAdapter+Utilities.swift in Sources */,
|
||||||
FD37E9FF28A5F2CD003AE748 /* Configuration.swift in Sources */,
|
FD37E9FF28A5F2CD003AE748 /* Configuration.swift in Sources */,
|
||||||
|
@ -6029,7 +6017,7 @@
|
||||||
FD17D7A127F40D2500122BE0 /* Storage.swift in Sources */,
|
FD17D7A127F40D2500122BE0 /* Storage.swift in Sources */,
|
||||||
FD1A94FB2900D1C2000D73D3 /* PersistableRecord+Utilities.swift in Sources */,
|
FD1A94FB2900D1C2000D73D3 /* PersistableRecord+Utilities.swift in Sources */,
|
||||||
FD5D201E27B0D87C00FEA984 /* SessionId.swift in Sources */,
|
FD5D201E27B0D87C00FEA984 /* SessionId.swift in Sources */,
|
||||||
C32C5A24256DB7DB003C73A2 /* SNUserDefaults.swift in Sources */,
|
C32C5A24256DB7DB003C73A2 /* UserDefaultsInfo.swift in Sources */,
|
||||||
FD8ECF922938552800C0D1BB /* Threading.swift in Sources */,
|
FD8ECF922938552800C0D1BB /* Threading.swift in Sources */,
|
||||||
B8856D7B256F14F4001CE70E /* UIView+OWS.m in Sources */,
|
B8856D7B256F14F4001CE70E /* UIView+OWS.m in Sources */,
|
||||||
FDF22211281B5E0B000A4995 /* TableRecord+Utilities.swift in Sources */,
|
FDF22211281B5E0B000A4995 /* TableRecord+Utilities.swift in Sources */,
|
||||||
|
|
|
@ -91,10 +91,13 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reportOutgoingCall(_ call: SessionCall) {
|
public func reportOutgoingCall(
|
||||||
|
_ call: SessionCall,
|
||||||
|
using dependencies: Dependencies = Dependencies()
|
||||||
|
) {
|
||||||
AssertIsOnMainThread()
|
AssertIsOnMainThread()
|
||||||
UserDefaults.sharedLokiProject?[.isCallOngoing] = true
|
dependencies[defaults: .appGroup, key: .isCallOngoing] = true
|
||||||
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = Date()
|
dependencies[defaults: .appGroup, key: .lastCallPreOffer] = Date()
|
||||||
|
|
||||||
call.stateDidChange = {
|
call.stateDidChange = {
|
||||||
if call.hasStartedConnecting {
|
if call.hasStartedConnecting {
|
||||||
|
@ -107,7 +110,12 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reportIncomingCall(_ call: SessionCall, callerName: String, completion: @escaping (Error?) -> Void) {
|
public func reportIncomingCall(
|
||||||
|
_ call: SessionCall,
|
||||||
|
callerName: String,
|
||||||
|
using dependencies: Dependencies = Dependencies(),
|
||||||
|
completion: @escaping (Error?) -> Void
|
||||||
|
) {
|
||||||
let provider = provider ?? Self.sharedProvider(useSystemCallLog: false)
|
let provider = provider ?? Self.sharedProvider(useSystemCallLog: false)
|
||||||
// Construct a CXCallUpdate describing the incoming call, including the caller.
|
// Construct a CXCallUpdate describing the incoming call, including the caller.
|
||||||
let update = CXCallUpdate()
|
let update = CXCallUpdate()
|
||||||
|
@ -124,8 +132,8 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
|
||||||
completion(error)
|
completion(error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
UserDefaults.sharedLokiProject?[.isCallOngoing] = true
|
dependencies[defaults: .appGroup, key: .isCallOngoing] = true
|
||||||
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = Date()
|
dependencies[defaults: .appGroup, key: .lastCallPreOffer] = Date()
|
||||||
completion(nil)
|
completion(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,8 +151,8 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
|
||||||
|
|
||||||
func handleCallEnded() {
|
func handleCallEnded() {
|
||||||
WebRTCSession.current = nil
|
WebRTCSession.current = nil
|
||||||
UserDefaults.sharedLokiProject?[.isCallOngoing] = false
|
dependencies[defaults: .appGroup, key: .isCallOngoing] = false
|
||||||
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = nil
|
dependencies[defaults: .appGroup, key: .lastCallPreOffer] = nil
|
||||||
|
|
||||||
if CurrentAppContext().isInBackground() {
|
if CurrentAppContext().isInBackground() {
|
||||||
(UIApplication.shared.delegate as? AppDelegate)?.stopPollers()
|
(UIApplication.shared.delegate as? AppDelegate)?.stopPollers()
|
||||||
|
|
|
@ -77,7 +77,7 @@ extension ConversationVC:
|
||||||
@objc func startCall(_ sender: Any?) {
|
@objc func startCall(_ sender: Any?) {
|
||||||
guard SessionCall.isEnabled else { return }
|
guard SessionCall.isEnabled else { return }
|
||||||
guard viewModel.threadData.threadIsBlocked == false else { return }
|
guard viewModel.threadData.threadIsBlocked == false else { return }
|
||||||
guard Dependencies()[singleton: .storage][.areCallsEnabled] else {
|
guard Dependencies()[singleton: .storage, key: .areCallsEnabled] else {
|
||||||
let confirmationModal: ConfirmationModal = ConfirmationModal(
|
let confirmationModal: ConfirmationModal = ConfirmationModal(
|
||||||
info: ConfirmationModal.Info(
|
info: ConfirmationModal.Info(
|
||||||
title: "modal_call_permission_request_title".localized(),
|
title: "modal_call_permission_request_title".localized(),
|
||||||
|
@ -240,7 +240,7 @@ extension ConversationVC:
|
||||||
// MARK: - ExpandingAttachmentsButtonDelegate
|
// MARK: - ExpandingAttachmentsButtonDelegate
|
||||||
|
|
||||||
func handleGIFButtonTapped() {
|
func handleGIFButtonTapped() {
|
||||||
guard Dependencies()[singleton: .storage][.isGiphyEnabled] else {
|
guard Dependencies()[singleton: .storage, key: .isGiphyEnabled] else {
|
||||||
let modal: ConfirmationModal = ConfirmationModal(
|
let modal: ConfirmationModal = ConfirmationModal(
|
||||||
info: ConfirmationModal.Info(
|
info: ConfirmationModal.Info(
|
||||||
title: "GIPHY_PERMISSION_TITLE".localized(),
|
title: "GIPHY_PERMISSION_TITLE".localized(),
|
||||||
|
@ -624,7 +624,7 @@ extension ConversationVC:
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMessageSent() {
|
func handleMessageSent() {
|
||||||
if Dependencies()[singleton: .storage][.playNotificationSoundInForeground] {
|
if Dependencies()[singleton: .storage, key: .playNotificationSoundInForeground] {
|
||||||
let soundID = Preferences.Sound.systemSoundId(for: .messageSent, quiet: true)
|
let soundID = Preferences.Sound.systemSoundId(for: .messageSent, quiet: true)
|
||||||
AudioServicesPlaySystemSound(soundID)
|
AudioServicesPlaySystemSound(soundID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1044,7 +1044,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
|
||||||
.firstIndex(where: { $0.id == interactionId }),
|
.firstIndex(where: { $0.id == interactionId }),
|
||||||
currentIndex < (messageSection.elements.count - 1),
|
currentIndex < (messageSection.elements.count - 1),
|
||||||
messageSection.elements[currentIndex + 1].cellType == .audio,
|
messageSection.elements[currentIndex + 1].cellType == .audio,
|
||||||
dependencies[singleton: .storage][.shouldAutoPlayConsecutiveAudioMessages] == true
|
dependencies[singleton: .storage, key: .shouldAutoPlayConsecutiveAudioMessages]
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
let nextItem: MessageViewModel = messageSection.elements[currentIndex + 1]
|
let nextItem: MessageViewModel = messageSection.elements[currentIndex + 1]
|
||||||
|
|
|
@ -291,15 +291,15 @@ final class InputView: UIView, InputViewButtonDelegate, InputTextViewDelegate, M
|
||||||
// Suggest that the user enable link previews if they haven't already and we haven't
|
// Suggest that the user enable link previews if they haven't already and we haven't
|
||||||
// told them about link previews yet
|
// told them about link previews yet
|
||||||
let text = inputTextView.text!
|
let text = inputTextView.text!
|
||||||
let areLinkPreviewsEnabled: Bool = dependencies[singleton: .storage][.areLinkPreviewsEnabled]
|
let areLinkPreviewsEnabled: Bool = dependencies[singleton: .storage, key: .areLinkPreviewsEnabled]
|
||||||
|
|
||||||
if
|
if
|
||||||
!LinkPreview.allPreviewUrls(forMessageBodyText: text).isEmpty &&
|
!LinkPreview.allPreviewUrls(forMessageBodyText: text).isEmpty &&
|
||||||
!areLinkPreviewsEnabled &&
|
!areLinkPreviewsEnabled &&
|
||||||
!UserDefaults.standard[.hasSeenLinkPreviewSuggestion]
|
!dependencies[defaults: .standard, key: .hasSeenLinkPreviewSuggestion]
|
||||||
{
|
{
|
||||||
delegate?.showLinkPreviewSuggestionModal()
|
delegate?.showLinkPreviewSuggestionModal()
|
||||||
UserDefaults.standard[.hasSeenLinkPreviewSuggestion] = true
|
dependencies[defaults: .standard, key: .hasSeenLinkPreviewSuggestion] = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Check that link previews are enabled
|
// Check that link previews are enabled
|
||||||
|
|
|
@ -141,7 +141,7 @@ final class CallMessageCell: MessageCell {
|
||||||
|
|
||||||
let shouldShowInfoIcon: Bool = (
|
let shouldShowInfoIcon: Bool = (
|
||||||
messageInfo.state == .permissionDenied &&
|
messageInfo.state == .permissionDenied &&
|
||||||
!Dependencies()[singleton: .storage][.areCallsEnabled]
|
!Dependencies()[singleton: .storage, key: .areCallsEnabled]
|
||||||
)
|
)
|
||||||
infoImageViewWidthConstraint.constant = (shouldShowInfoIcon ? CallMessageCell.iconSize : 0)
|
infoImageViewWidthConstraint.constant = (shouldShowInfoIcon ? CallMessageCell.iconSize : 0)
|
||||||
infoImageViewHeightConstraint.constant = (shouldShowInfoIcon ? CallMessageCell.iconSize : 0)
|
infoImageViewHeightConstraint.constant = (shouldShowInfoIcon ? CallMessageCell.iconSize : 0)
|
||||||
|
@ -177,7 +177,7 @@ final class CallMessageCell: MessageCell {
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
// Should only be tappable if the info icon is visible
|
// Should only be tappable if the info icon is visible
|
||||||
guard messageInfo.state == .permissionDenied && !Dependencies()[singleton: .storage][.areCallsEnabled] else { return }
|
guard messageInfo.state == .permissionDenied && !Dependencies()[singleton: .storage, key: .areCallsEnabled] else { return }
|
||||||
|
|
||||||
self.delegate?.handleItemTapped(cellViewModel, gestureRecognizer: gestureRecognizer)
|
self.delegate?.handleItemTapped(cellViewModel, gestureRecognizer: gestureRecognizer)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
// Called via the OS so create a default 'Dependencies' instance
|
// Called via the OS so create a default 'Dependencies' instance
|
||||||
let dependencies: Dependencies = Dependencies()
|
let dependencies: Dependencies = Dependencies()
|
||||||
Cryptography.seedRandom()
|
Cryptography.seedRandom()
|
||||||
AppVersion.sharedInstance()
|
AppVersion.configure(using: dependencies)
|
||||||
AppEnvironment.shared.pushRegistrationManager.createVoipRegistryIfNecessary()
|
AppEnvironment.shared.pushRegistrationManager.createVoipRegistryIfNecessary()
|
||||||
|
|
||||||
// Prevent the device from sleeping during database view async registration
|
// Prevent the device from sleeping during database view async registration
|
||||||
|
@ -99,8 +99,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
)
|
)
|
||||||
|
|
||||||
if Environment.shared?.callManager.wrappedValue?.currentCall == nil {
|
if Environment.shared?.callManager.wrappedValue?.currentCall == nil {
|
||||||
UserDefaults.sharedLokiProject?[.isCallOngoing] = false
|
dependencies[defaults: .appGroup, key: .isCallOngoing] = false
|
||||||
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = nil
|
dependencies[defaults: .appGroup, key: .lastCallPreOffer] = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// No point continuing if we are running tests
|
// No point continuing if we are running tests
|
||||||
|
@ -127,7 +127,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
)
|
)
|
||||||
NotificationCenter.default.addObserver(
|
NotificationCenter.default.addObserver(
|
||||||
self,
|
self,
|
||||||
selector: #selector(showMissedCallTipsIfNeeded(_:)),
|
selector: #selector(showMissedCallTipsIfNeededNotification(_:)),
|
||||||
name: .missedCall,
|
name: .missedCall,
|
||||||
object: nil
|
object: nil
|
||||||
)
|
)
|
||||||
|
@ -236,7 +236,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
|
|
||||||
// Called via the OS so create a default 'Dependencies' instance
|
// Called via the OS so create a default 'Dependencies' instance
|
||||||
let dependencies: Dependencies = Dependencies()
|
let dependencies: Dependencies = Dependencies()
|
||||||
UserDefaults.sharedLokiProject?[.isMainAppActive] = true
|
dependencies[defaults: .appGroup, key: .isMainAppActive] = true
|
||||||
|
|
||||||
ensureRootViewController(calledFrom: .didBecomeActive, using: dependencies)
|
ensureRootViewController(calledFrom: .didBecomeActive, using: dependencies)
|
||||||
|
|
||||||
|
@ -259,9 +259,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillResignActive(_ application: UIApplication) {
|
func applicationWillResignActive(_ application: UIApplication) {
|
||||||
clearAllNotificationsAndRestoreBadgeCount()
|
// Called via the OS so create a default 'Dependencies' instance
|
||||||
|
let dependencies: Dependencies = Dependencies()
|
||||||
|
|
||||||
UserDefaults.sharedLokiProject?[.isMainAppActive] = false
|
clearAllNotificationsAndRestoreBadgeCount(using: dependencies)
|
||||||
|
|
||||||
|
dependencies[defaults: .appGroup, key: .isMainAppActive] = false
|
||||||
|
|
||||||
DDLog.flushLog()
|
DDLog.flushLog()
|
||||||
}
|
}
|
||||||
|
@ -374,7 +377,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
DeviceSleepManager.sharedInstance.removeBlock(blockObject: self)
|
DeviceSleepManager.sharedInstance.removeBlock(blockObject: self)
|
||||||
|
|
||||||
/// App launch hasn't really completed until the main screen is loaded so wait until then to register it
|
/// App launch hasn't really completed until the main screen is loaded so wait until then to register it
|
||||||
AppVersion.sharedInstance().mainAppLaunchDidComplete()
|
AppVersion.shared.mainAppLaunchDidComplete(using: dependencies)
|
||||||
|
|
||||||
/// App won't be ready for extensions and no need to enqueue a config sync unless we successfully completed startup
|
/// App won't be ready for extensions and no need to enqueue a config sync unless we successfully completed startup
|
||||||
dependencies[singleton: .storage].writeAsync { db in
|
dependencies[singleton: .storage].writeAsync { db in
|
||||||
|
@ -387,13 +390,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
db[.isReadyForAppExtensions] = true
|
db[.isReadyForAppExtensions] = true
|
||||||
|
|
||||||
if Identity.userCompletedRequiredOnboarding(db) {
|
if Identity.userCompletedRequiredOnboarding(db) {
|
||||||
let appVersion: AppVersion = AppVersion.sharedInstance()
|
|
||||||
|
|
||||||
// If the device needs to sync config or the user updated to a new version
|
// If the device needs to sync config or the user updated to a new version
|
||||||
if
|
if
|
||||||
needsConfigSync || (
|
needsConfigSync || (
|
||||||
(appVersion.lastAppVersion?.count ?? 0) > 0 &&
|
(AppVersion.shared.lastAppVersion?.count ?? 0) > 0 &&
|
||||||
appVersion.lastAppVersion != appVersion.currentAppVersion
|
AppVersion.shared.lastAppVersion != AppVersion.shared.currentAppVersion
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ConfigurationSyncJob.enqueue(db, publicKey: getUserHexEncodedPublicKey(db))
|
ConfigurationSyncJob.enqueue(db, publicKey: getUserHexEncodedPublicKey(db))
|
||||||
|
@ -539,7 +540,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
/// called again - this can result in odd behaviours so hold off on running this logic until it's properly called again
|
/// called again - this can result in odd behaviours so hold off on running this logic until it's properly called again
|
||||||
guard
|
guard
|
||||||
Identity.userExists(using: dependencies) &&
|
Identity.userExists(using: dependencies) &&
|
||||||
UserDefaults.sharedLokiProject?[.isMainAppActive] == true
|
dependencies[defaults: .appGroup, key: .isMainAppActive] == true
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
enableBackgroundRefreshIfNecessary()
|
enableBackgroundRefreshIfNecessary()
|
||||||
|
@ -589,7 +590,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
let presentedViewController: UIViewController? = self?.window?.rootViewController?.presentedViewController
|
let presentedViewController: UIViewController? = self?.window?.rootViewController?.presentedViewController
|
||||||
let targetRootViewController: UIViewController = TopBannerController(
|
let targetRootViewController: UIViewController = TopBannerController(
|
||||||
child: StyledNavigationController(rootViewController: rootViewController),
|
child: StyledNavigationController(rootViewController: rootViewController),
|
||||||
cachedWarning: UserDefaults.sharedLokiProject?[.topBannerWarningToShow]
|
cachedWarning: dependencies[defaults: .appGroup, key: .topBannerWarningToShow]
|
||||||
.map { rawValue in TopBannerController.Warning(rawValue: rawValue) }
|
.map { rawValue in TopBannerController.Warning(rawValue: rawValue) }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -687,7 +688,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private func clearAllNotificationsAndRestoreBadgeCount(using dependencies: Dependencies = Dependencies()) {
|
private func clearAllNotificationsAndRestoreBadgeCount(using dependencies: Dependencies) {
|
||||||
AppReadiness.runNowOrWhenAppDidBecomeReady {
|
AppReadiness.runNowOrWhenAppDidBecomeReady {
|
||||||
AppEnvironment.shared.notificationPresenter.clearAllNotifications()
|
AppEnvironment.shared.notificationPresenter.clearAllNotifications()
|
||||||
|
|
||||||
|
@ -760,11 +761,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
handleActivation()
|
handleActivation()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func showMissedCallTipsIfNeeded(_ notification: Notification) {
|
@objc public func showMissedCallTipsIfNeededNotification(_ notification: Notification) {
|
||||||
guard !UserDefaults.standard[.hasSeenCallMissedTips] else { return }
|
showMissedCallTipsIfNeeded(notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showMissedCallTipsIfNeeded(
|
||||||
|
_ notification: Notification,
|
||||||
|
using dependencies: Dependencies = Dependencies()
|
||||||
|
) {
|
||||||
|
guard !dependencies[defaults: .standard, key: .hasSeenCallMissedTips] else { return }
|
||||||
guard Thread.isMainThread else {
|
guard Thread.isMainThread else {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.showMissedCallTipsIfNeeded(notification)
|
self.showMissedCallTipsIfNeeded(notification, using: dependencies)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -778,7 +786,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
)
|
)
|
||||||
presentingVC.present(callMissedTipsModal, animated: true, completion: nil)
|
presentingVC.present(callMissedTipsModal, animated: true, completion: nil)
|
||||||
|
|
||||||
UserDefaults.standard[.hasSeenCallMissedTips] = true
|
dependencies[defaults: .standard, key: .hasSeenCallMissedTips] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Polling
|
// MARK: - Polling
|
||||||
|
|
|
@ -239,10 +239,4 @@ final class MainAppContext: NSObject, AppContext {
|
||||||
|
|
||||||
return (targetPath ?? "")
|
return (targetPath ?? "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func appUserDefaults() -> UserDefaults {
|
|
||||||
owsAssertDebug(UserDefaults.sharedLokiProject != nil)
|
|
||||||
|
|
||||||
return (UserDefaults.sharedLokiProject ?? UserDefaults.standard)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -512,7 +512,7 @@ public class NotificationPresenter: NotificationsProtocol {
|
||||||
|
|
||||||
private func checkIfShouldPlaySound(applicationState: UIApplication.State) -> Bool {
|
private func checkIfShouldPlaySound(applicationState: UIApplication.State) -> Bool {
|
||||||
guard applicationState == .active else { return true }
|
guard applicationState == .active else { return true }
|
||||||
guard Dependencies()[singleton: .storage][.playNotificationSoundInForeground] else { return false }
|
guard Dependencies()[singleton: .storage, key: .playNotificationSoundInForeground] else { return false }
|
||||||
|
|
||||||
let nowMs: UInt64 = UInt64(floor(Date().timeIntervalSince1970 * 1000))
|
let nowMs: UInt64 = UInt64(floor(Date().timeIntervalSince1970 * 1000))
|
||||||
let recentThreshold = nowMs - UInt64(kAudioNotificationsThrottleInterval * Double(kSecondInMs))
|
let recentThreshold = nowMs - UInt64(kAudioNotificationsThrottleInterval * Double(kSecondInMs))
|
||||||
|
|
|
@ -23,7 +23,7 @@ public enum SyncPushTokensJob: JobExecutor {
|
||||||
using dependencies: Dependencies = Dependencies()
|
using dependencies: Dependencies = Dependencies()
|
||||||
) {
|
) {
|
||||||
// Don't run when inactive or not in main app or if the user doesn't exist yet
|
// Don't run when inactive or not in main app or if the user doesn't exist yet
|
||||||
guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
|
guard dependencies[defaults: .appGroup, key: .isMainAppActive] else {
|
||||||
return deferred(job, dependencies) // Don't need to do anything if it's not the main app
|
return deferred(job, dependencies) // Don't need to do anything if it's not the main app
|
||||||
}
|
}
|
||||||
guard Identity.userCompletedRequiredOnboarding() else {
|
guard Identity.userCompletedRequiredOnboarding() else {
|
||||||
|
@ -32,12 +32,12 @@ public enum SyncPushTokensJob: JobExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if the device has 'Fast Mode' (APNS) enabled
|
// Determine if the device has 'Fast Mode' (APNS) enabled
|
||||||
let isUsingFullAPNs: Bool = UserDefaults.standard[.isUsingFullAPNs]
|
let isUsingFullAPNs: Bool = dependencies[defaults: .standard, key: .isUsingFullAPNs]
|
||||||
|
|
||||||
// If the job is running and 'Fast Mode' is disabled then we should try to unregister the existing
|
// If the job is running and 'Fast Mode' is disabled then we should try to unregister the existing
|
||||||
// token
|
// token
|
||||||
guard isUsingFullAPNs else {
|
guard isUsingFullAPNs else {
|
||||||
Just(dependencies[singleton: .storage][.lastRecordedPushToken])
|
Just(dependencies[singleton: .storage, key: .lastRecordedPushToken])
|
||||||
.setFailureType(to: Error.self)
|
.setFailureType(to: Error.self)
|
||||||
.flatMap { lastRecordedPushToken -> AnyPublisher<Void, Error> in
|
.flatMap { lastRecordedPushToken -> AnyPublisher<Void, Error> in
|
||||||
// Tell the device to unregister for remote notifications (essentially try to invalidate
|
// Tell the device to unregister for remote notifications (essentially try to invalidate
|
||||||
|
@ -92,7 +92,7 @@ public enum SyncPushTokensJob: JobExecutor {
|
||||||
/// • We want to force an update
|
/// • We want to force an update
|
||||||
let timeSinceLastSubscription: TimeInterval = dependencies.dateNow
|
let timeSinceLastSubscription: TimeInterval = dependencies.dateNow
|
||||||
.timeIntervalSince(
|
.timeIntervalSince(
|
||||||
dependencies.standardUserDefaults[.lastPushNotificationSync]
|
dependencies[defaults: .standard, key: .lastPushNotificationSync]
|
||||||
.defaulting(to: Date.distantPast)
|
.defaulting(to: Date.distantPast)
|
||||||
)
|
)
|
||||||
let uploadOnlyIfStale: Bool? = {
|
let uploadOnlyIfStale: Bool? = {
|
||||||
|
@ -106,7 +106,7 @@ public enum SyncPushTokensJob: JobExecutor {
|
||||||
|
|
||||||
guard
|
guard
|
||||||
timeSinceLastSubscription >= SyncPushTokensJob.maxFrequency ||
|
timeSinceLastSubscription >= SyncPushTokensJob.maxFrequency ||
|
||||||
dependencies.storage[.lastRecordedPushToken] != pushToken ||
|
dependencies[singleton: .storage, key: .lastRecordedPushToken] != pushToken ||
|
||||||
uploadOnlyIfStale == false
|
uploadOnlyIfStale == false
|
||||||
else {
|
else {
|
||||||
SNLog("[SyncPushTokensJob] OS subscription completed, skipping server subscription due to frequency")
|
SNLog("[SyncPushTokensJob] OS subscription completed, skipping server subscription due to frequency")
|
||||||
|
@ -131,7 +131,7 @@ public enum SyncPushTokensJob: JobExecutor {
|
||||||
case .finished:
|
case .finished:
|
||||||
Logger.warn("Recording push tokens locally. pushToken: \(redact(pushToken)), voipToken: \(redact(voipToken))")
|
Logger.warn("Recording push tokens locally. pushToken: \(redact(pushToken)), voipToken: \(redact(voipToken))")
|
||||||
SNLog("[SyncPushTokensJob] Completed")
|
SNLog("[SyncPushTokensJob] Completed")
|
||||||
dependencies[singleton: .standardUserDefaults][.lastPushNotificationSync] = dependencies.dateNow
|
dependencies[defaults: .standard, key: .lastPushNotificationSync] = dependencies.dateNow
|
||||||
|
|
||||||
dependencies[singleton: .storage].write(using: dependencies) { db in
|
dependencies[singleton: .storage].write(using: dependencies) { db in
|
||||||
db[.lastRecordedPushToken] = pushToken
|
db[.lastRecordedPushToken] = pushToken
|
||||||
|
|
|
@ -102,7 +102,7 @@ enum Onboarding {
|
||||||
profileNameRetrievalIdentifier.mutate { $0 = nil }
|
profileNameRetrievalIdentifier.mutate { $0 = nil }
|
||||||
profileNameRetrievalPublisher.mutate { $0 = nil }
|
profileNameRetrievalPublisher.mutate { $0 = nil }
|
||||||
|
|
||||||
dependencies[singleton: .standardUserDefaults][.hasSyncedInitialConfiguration] = false
|
dependencies[defaults: .standard, key: .hasSyncedInitialConfiguration] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func preregister(
|
func preregister(
|
||||||
|
@ -164,7 +164,7 @@ enum Onboarding {
|
||||||
// home screen a configuration sync is triggered (yes, the logic is a
|
// home screen a configuration sync is triggered (yes, the logic is a
|
||||||
// bit weird). This is needed so that if the user registers and
|
// bit weird). This is needed so that if the user registers and
|
||||||
// immediately links a device, there'll be a configuration in their swarm.
|
// immediately links a device, there'll be a configuration in their swarm.
|
||||||
dependencies[singleton: .standardUserDefaults][.hasSyncedInitialConfiguration] = (self == .register)
|
dependencies[defaults: .standard, key: .hasSyncedInitialConfiguration] = (self == .register)
|
||||||
|
|
||||||
// Only continue if this isn't a new account
|
// Only continue if this isn't a new account
|
||||||
guard self != .register else { return }
|
guard self != .register else { return }
|
||||||
|
|
|
@ -85,7 +85,7 @@ final class PNModeVC: BaseVC, OptionViewDelegate {
|
||||||
let registerButton = SessionButton(style: .filled, size: .large)
|
let registerButton = SessionButton(style: .filled, size: .large)
|
||||||
registerButton.accessibilityLabel = "Continue with settings"
|
registerButton.accessibilityLabel = "Continue with settings"
|
||||||
registerButton.setTitle("continue_2".localized(), for: .normal)
|
registerButton.setTitle("continue_2".localized(), for: .normal)
|
||||||
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
|
registerButton.addTarget(self, action: #selector(registerTapped), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
// Set up register button container
|
// Set up register button container
|
||||||
let registerButtonContainer = UIView(wrapping: registerButton, withInsets: UIEdgeInsets(top: 0, leading: Values.massiveSpacing, bottom: 0, trailing: Values.massiveSpacing), shouldAdaptForIPadWithWidth: Values.iPadButtonWidth)
|
let registerButtonContainer = UIView(wrapping: registerButton, withInsets: UIEdgeInsets(top: 0, leading: Values.massiveSpacing, bottom: 0, trailing: Values.massiveSpacing), shouldAdaptForIPadWithWidth: Values.iPadButtonWidth)
|
||||||
|
@ -128,7 +128,9 @@ final class PNModeVC: BaseVC, OptionViewDelegate {
|
||||||
optionViews.filter { $0 != optionView }.forEach { $0.isSelected = false }
|
optionViews.filter { $0 != optionView }.forEach { $0.isSelected = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func register() {
|
@objc private func registerTapped() { register() }
|
||||||
|
|
||||||
|
private func register(using dependencies: Dependencies = Dependencies()) {
|
||||||
guard selectedOptionView != nil else {
|
guard selectedOptionView != nil else {
|
||||||
let modal: ConfirmationModal = ConfirmationModal(
|
let modal: ConfirmationModal = ConfirmationModal(
|
||||||
targetView: self.view,
|
targetView: self.view,
|
||||||
|
@ -141,7 +143,7 @@ final class PNModeVC: BaseVC, OptionViewDelegate {
|
||||||
self.present(modal, animated: true)
|
self.present(modal, animated: true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
UserDefaults.standard[.isUsingFullAPNs] = (selectedOptionView == apnsOptionView)
|
dependencies[defaults: .standard, key: .isUsingFullAPNs] = (selectedOptionView == apnsOptionView)
|
||||||
|
|
||||||
// If we are registering then we can just continue on
|
// If we are registering then we can just continue on
|
||||||
guard flow != .register else {
|
guard flow != .register else {
|
||||||
|
@ -155,8 +157,7 @@ final class PNModeVC: BaseVC, OptionViewDelegate {
|
||||||
|
|
||||||
// Check if we already have a profile name (ie. profile retrieval completed while waiting on
|
// Check if we already have a profile name (ie. profile retrieval completed while waiting on
|
||||||
// this screen)
|
// this screen)
|
||||||
let existingProfileName: String? = Dependencies()[singleton: .storage]
|
let existingProfileName: String? = dependencies[singleton: .storage].read { db in
|
||||||
.read { db in
|
|
||||||
try Profile
|
try Profile
|
||||||
.filter(id: getUserHexEncodedPublicKey(db))
|
.filter(id: getUserHexEncodedPublicKey(db))
|
||||||
.select(.name)
|
.select(.name)
|
||||||
|
|
|
@ -86,9 +86,9 @@ class NotificationSettingsViewModel: SessionTableViewModel<NoNav, NotificationSe
|
||||||
.handleEvents(didFail: { SNLog("[NotificationSettingsViewModel] Observation failed with error: \($0)") })
|
.handleEvents(didFail: { SNLog("[NotificationSettingsViewModel] Observation failed with error: \($0)") })
|
||||||
.publisher(in: dependencies[singleton: .storage], scheduling: dependencies[singleton: .scheduler])
|
.publisher(in: dependencies[singleton: .storage], scheduling: dependencies[singleton: .scheduler])
|
||||||
.manualRefreshFrom(forcedRefresh)
|
.manualRefreshFrom(forcedRefresh)
|
||||||
.map { dbState -> State in
|
.map { [dependencies] dbState -> State in
|
||||||
State(
|
State(
|
||||||
isUsingFullAPNs: UserDefaults.standard[.isUsingFullAPNs],
|
isUsingFullAPNs: dependencies[defaults: .standard, key: .isUsingFullAPNs],
|
||||||
notificationSound: dbState.notificationSound,
|
notificationSound: dbState.notificationSound,
|
||||||
playNotificationSoundInForeground: dbState.playNotificationSoundInForeground,
|
playNotificationSoundInForeground: dbState.playNotificationSoundInForeground,
|
||||||
previewType: dbState.previewType
|
previewType: dbState.previewType
|
||||||
|
|
|
@ -262,8 +262,8 @@ final class NukeDataModal: Modal {
|
||||||
|
|
||||||
private func deleteAllLocalData(using dependencies: Dependencies = Dependencies()) {
|
private func deleteAllLocalData(using dependencies: Dependencies = Dependencies()) {
|
||||||
// Unregister push notifications if needed
|
// Unregister push notifications if needed
|
||||||
let isUsingFullAPNs: Bool = UserDefaults.standard[.isUsingFullAPNs]
|
let isUsingFullAPNs: Bool = dependencies[defaults: .standard, key: .isUsingFullAPNs]
|
||||||
let maybeDeviceToken: String? = UserDefaults.standard[.deviceToken]
|
let maybeDeviceToken: String? = dependencies[defaults: .standard, key: .deviceToken]
|
||||||
|
|
||||||
if isUsingFullAPNs, let deviceToken: String = maybeDeviceToken {
|
if isUsingFullAPNs, let deviceToken: String = maybeDeviceToken {
|
||||||
PushNotificationAPI
|
PushNotificationAPI
|
||||||
|
@ -298,11 +298,11 @@ final class NukeDataModal: Modal {
|
||||||
|
|
||||||
// Call through to the SessionApp's "resetAppData" which will wipe out logs, database and
|
// Call through to the SessionApp's "resetAppData" which will wipe out logs, database and
|
||||||
// profile storage
|
// profile storage
|
||||||
let wasUnlinked: Bool = UserDefaults.standard[.wasUnlinked]
|
let wasUnlinked: Bool = dependencies[defaults: .standard, key: .wasUnlinked]
|
||||||
|
|
||||||
SessionApp.resetAppData {
|
SessionApp.resetAppData {
|
||||||
// Resetting the data clears the old user defaults. We need to restore the unlink default.
|
// Resetting the data clears the old user defaults. We need to restore the unlink default.
|
||||||
UserDefaults.standard[.wasUnlinked] = wasUnlinked
|
dependencies[defaults: .standard, key: .wasUnlinked] = wasUnlinked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,7 +168,7 @@ class ScreenLockUI {
|
||||||
// until the app is ready.
|
// until the app is ready.
|
||||||
AppReadiness.runNowOrWhenAppWillBecomeReady { [weak self] in
|
AppReadiness.runNowOrWhenAppWillBecomeReady { [weak self] in
|
||||||
DispatchQueue.global(qos: .background).async {
|
DispatchQueue.global(qos: .background).async {
|
||||||
self?.isScreenLockLocked.mutate { $0 = Dependencies()[singleton: .storage][.isScreenLockEnabled] }
|
self?.isScreenLockLocked.mutate { $0 = Dependencies()[singleton: .storage, key: .isScreenLockEnabled] }
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self?.ensureUI()
|
self?.ensureUI()
|
||||||
|
@ -189,7 +189,7 @@ class ScreenLockUI {
|
||||||
Logger.verbose("tryToActivateScreenLockUponBecomingActive NO 0")
|
Logger.verbose("tryToActivateScreenLockUponBecomingActive NO 0")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard Dependencies()[singleton: .storage][.isScreenLockEnabled] else {
|
guard Dependencies()[singleton: .storage, key: .isScreenLockEnabled] else {
|
||||||
// Screen lock is not enabled.
|
// Screen lock is not enabled.
|
||||||
Logger.verbose("tryToActivateScreenLockUponBecomingActive NO 1")
|
Logger.verbose("tryToActivateScreenLockUponBecomingActive NO 1")
|
||||||
return;
|
return;
|
||||||
|
@ -378,7 +378,7 @@ class ScreenLockUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.global(qos: .background).async {
|
DispatchQueue.global(qos: .background).async {
|
||||||
self.isScreenLockLocked.mutate { $0 = Dependencies()[singleton: .storage][.isScreenLockEnabled] }
|
self.isScreenLockLocked.mutate { $0 = Dependencies()[singleton: .storage, key: .isScreenLockEnabled] }
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
// NOTE: this notifications fires _before_ applicationDidBecomeActive,
|
// NOTE: this notifications fires _before_ applicationDidBecomeActive,
|
||||||
|
|
|
@ -58,7 +58,7 @@ public struct ConfigDump: Codable, Equatable, Hashable, FetchableRecord, Persist
|
||||||
|
|
||||||
// MARK: - Convenience
|
// MARK: - Convenience
|
||||||
|
|
||||||
public extension ConfigDump.Variant, CustomStringConvertible {
|
public extension ConfigDump.Variant {
|
||||||
static let userVariants: Set<ConfigDump.Variant> = [
|
static let userVariants: Set<ConfigDump.Variant> = [
|
||||||
.userProfile, .contacts, .convoInfoVolatile, .userGroups
|
.userProfile, .contacts, .convoInfoVolatile, .userGroups
|
||||||
]
|
]
|
||||||
|
@ -132,7 +132,9 @@ public extension ConfigDump.Variant, CustomStringConvertible {
|
||||||
default: return 1
|
default: return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ConfigDump.Variant: CustomStringConvertible {
|
||||||
public var description: String {
|
public var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .userProfile: return "userProfile"
|
case .userProfile: return "userProfile"
|
||||||
|
|
|
@ -216,7 +216,7 @@ public extension LinkPreview {
|
||||||
selectedRange: NSRange? = nil,
|
selectedRange: NSRange? = nil,
|
||||||
using dependencies: Dependencies = Dependencies()
|
using dependencies: Dependencies = Dependencies()
|
||||||
) -> String? {
|
) -> String? {
|
||||||
guard dependencies[singleton: .storage][.areLinkPreviewsEnabled] else { return nil }
|
guard dependencies[singleton: .storage, key: .areLinkPreviewsEnabled] else { return nil }
|
||||||
guard let body: String = body else { return nil }
|
guard let body: String = body else { return nil }
|
||||||
|
|
||||||
if let cachedUrl = previewUrlCache.wrappedValue.object(forKey: body as NSString) as String? {
|
if let cachedUrl = previewUrlCache.wrappedValue.object(forKey: body as NSString) as String? {
|
||||||
|
@ -297,7 +297,7 @@ public extension LinkPreview {
|
||||||
|
|
||||||
// Exit early if link previews are not enabled in order to avoid
|
// Exit early if link previews are not enabled in order to avoid
|
||||||
// tainting the cache.
|
// tainting the cache.
|
||||||
guard dependencies[singleton: .storage][.areLinkPreviewsEnabled] else { return }
|
guard dependencies[singleton: .storage, key: .areLinkPreviewsEnabled] else { return }
|
||||||
|
|
||||||
serialQueue.sync {
|
serialQueue.sync {
|
||||||
linkPreviewDraftCache = linkPreviewDraft
|
linkPreviewDraftCache = linkPreviewDraft
|
||||||
|
@ -308,7 +308,7 @@ public extension LinkPreview {
|
||||||
previewUrl: String?,
|
previewUrl: String?,
|
||||||
using dependencies: Dependencies = Dependencies()
|
using dependencies: Dependencies = Dependencies()
|
||||||
) -> AnyPublisher<LinkPreviewDraft, Error> {
|
) -> AnyPublisher<LinkPreviewDraft, Error> {
|
||||||
guard dependencies[singleton: .storage][.areLinkPreviewsEnabled] else {
|
guard dependencies[singleton: .storage, key: .areLinkPreviewsEnabled] else {
|
||||||
return Fail(error: LinkPreviewError.featureDisabled)
|
return Fail(error: LinkPreviewError.featureDisabled)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,12 @@ public enum DisappearingMessagesJob: JobExecutor {
|
||||||
// MARK: - Convenience
|
// MARK: - Convenience
|
||||||
|
|
||||||
public extension DisappearingMessagesJob {
|
public extension DisappearingMessagesJob {
|
||||||
@discardableResult static func updateNextRunIfNeeded(_ db: Database) -> Job? {
|
@discardableResult static func updateNextRunIfNeeded(
|
||||||
|
_ db: Database,
|
||||||
|
using dependencies: Dependencies = Dependencies()
|
||||||
|
) -> Job? {
|
||||||
// Don't run when inactive or not in main app
|
// Don't run when inactive or not in main app
|
||||||
guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else { return nil }
|
guard dependencies[defaults: .appGroup, key: .isMainAppActive] else { return nil }
|
||||||
|
|
||||||
// If there is another expiring message then update the job to run 1 second after it's meant to expire
|
// If there is another expiring message then update the job to run 1 second after it's meant to expire
|
||||||
let nextExpirationTimestampMs: Double? = try? Interaction
|
let nextExpirationTimestampMs: Double? = try? Interaction
|
||||||
|
|
|
@ -40,7 +40,7 @@ public enum GarbageCollectionJob: JobExecutor {
|
||||||
/// app at about the same time every day will trigger the garbage collection) - since this runs when the app becomes active we
|
/// app at about the same time every day will trigger the garbage collection) - since this runs when the app becomes active we
|
||||||
/// want to prevent it running to frequently (the app becomes active if a system alert, the notification center or the control panel
|
/// want to prevent it running to frequently (the app becomes active if a system alert, the notification center or the control panel
|
||||||
/// are shown)
|
/// are shown)
|
||||||
let lastGarbageCollection: Date = dependencies[singleton: .standardUserDefaults][.lastGarbageCollection]
|
let lastGarbageCollection: Date = dependencies[defaults: .standard, key: .lastGarbageCollection]
|
||||||
.defaulting(to: Date.distantPast)
|
.defaulting(to: Date.distantPast)
|
||||||
let finalTypesToCollect: Set<Types> = {
|
let finalTypesToCollect: Set<Types> = {
|
||||||
guard
|
guard
|
||||||
|
@ -461,7 +461,7 @@ public enum GarbageCollectionJob: JobExecutor {
|
||||||
// If we did a full collection then update the 'lastGarbageCollection' date to
|
// If we did a full collection then update the 'lastGarbageCollection' date to
|
||||||
// prevent a full collection from running again in the next 23 hours
|
// prevent a full collection from running again in the next 23 hours
|
||||||
if job.behaviour == .recurringOnActive && dependencies.dateNow.timeIntervalSince(lastGarbageCollection) > (23 * 60 * 60) {
|
if job.behaviour == .recurringOnActive && dependencies.dateNow.timeIntervalSince(lastGarbageCollection) > (23 * 60 * 60) {
|
||||||
dependencies[singleton: .standardUserDefaults][.lastGarbageCollection] = dependencies.dateNow
|
dependencies[defaults: .standard, key: .lastGarbageCollection] = dependencies.dateNow
|
||||||
}
|
}
|
||||||
|
|
||||||
success(job, false, dependencies)
|
success(job, false, dependencies)
|
||||||
|
|
|
@ -18,7 +18,7 @@ public enum RetrieveDefaultOpenGroupRoomsJob: JobExecutor {
|
||||||
using dependencies: Dependencies
|
using dependencies: Dependencies
|
||||||
) {
|
) {
|
||||||
// Don't run when inactive or not in main app
|
// Don't run when inactive or not in main app
|
||||||
guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
|
guard dependencies[defaults: .appGroup, key: .isMainAppActive] else {
|
||||||
deferred(job, dependencies) // Don't need to do anything if it's not the main app
|
deferred(job, dependencies) // Don't need to do anything if it's not the main app
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,13 @@ public enum UpdateProfilePictureJob: JobExecutor {
|
||||||
using dependencies: Dependencies
|
using dependencies: Dependencies
|
||||||
) {
|
) {
|
||||||
// Don't run when inactive or not in main app
|
// Don't run when inactive or not in main app
|
||||||
guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
|
guard dependencies[defaults: .appGroup, key: .isMainAppActive] else {
|
||||||
return deferred(job, dependencies) // Don't need to do anything if it's not the main app
|
return deferred(job, dependencies) // Don't need to do anything if it's not the main app
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only re-upload the profile picture if enough time has passed since the last upload
|
// Only re-upload the profile picture if enough time has passed since the last upload
|
||||||
guard
|
guard
|
||||||
let lastProfilePictureUpload: Date = dependencies[singleton: .standardUserDefaults][.lastProfilePictureUpload],
|
let lastProfilePictureUpload: Date = dependencies[defaults: .standard, key: .lastProfilePictureUpload],
|
||||||
dependencies.dateNow.timeIntervalSince(lastProfilePictureUpload) > (14 * 24 * 60 * 60)
|
dependencies.dateNow.timeIntervalSince(lastProfilePictureUpload) > (14 * 24 * 60 * 60)
|
||||||
else {
|
else {
|
||||||
// Reset the `nextRunTimestamp` value just in case the last run failed so we don't get stuck
|
// Reset the `nextRunTimestamp` value just in case the last run failed so we don't get stuck
|
||||||
|
|
|
@ -1117,7 +1117,7 @@ public final class OpenGroupManager {
|
||||||
// don't double up on fetch requests by storing the existing request as a promise if
|
// don't double up on fetch requests by storing the existing request as a promise if
|
||||||
// there is one.
|
// there is one.
|
||||||
let threadId: String = OpenGroup.idFor(roomToken: roomToken, server: server)
|
let threadId: String = OpenGroup.idFor(roomToken: roomToken, server: server)
|
||||||
let lastOpenGroupImageUpdate: Date? = dependencies[singleton: .standardUserDefaults][.lastOpenGroupImageUpdate]
|
let lastOpenGroupImageUpdate: Date? = dependencies[defaults: .standard, key: .lastOpenGroupImageUpdate]
|
||||||
let now: Date = dependencies.dateNow
|
let now: Date = dependencies.dateNow
|
||||||
let timeSinceLastUpdate: TimeInterval = (lastOpenGroupImageUpdate.map { now.timeIntervalSince($0) } ?? .greatestFiniteMagnitude)
|
let timeSinceLastUpdate: TimeInterval = (lastOpenGroupImageUpdate.map { now.timeIntervalSince($0) } ?? .greatestFiniteMagnitude)
|
||||||
let updateInterval: TimeInterval = (7 * 24 * 60 * 60)
|
let updateInterval: TimeInterval = (7 * 24 * 60 * 60)
|
||||||
|
@ -1198,7 +1198,7 @@ public final class OpenGroupManager {
|
||||||
.filter(id: threadId)
|
.filter(id: threadId)
|
||||||
.updateAll(db, OpenGroup.Columns.imageData.set(to: imageData))
|
.updateAll(db, OpenGroup.Columns.imageData.set(to: imageData))
|
||||||
}
|
}
|
||||||
dependencies[singleton: .standardUserDefaults][.lastOpenGroupImageUpdate] = now
|
dependencies[defaults: .standard, key: .lastOpenGroupImageUpdate] = now
|
||||||
}
|
}
|
||||||
|
|
||||||
resolver(Result.success(imageData))
|
resolver(Result.success(imageData))
|
||||||
|
@ -1244,7 +1244,7 @@ public extension OpenGroupManager {
|
||||||
return storedTimeSinceLastOpen
|
return storedTimeSinceLastOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let lastOpen: Date = dependencies[singleton: .standardUserDefaults][.lastOpen] else {
|
guard let lastOpen: Date = dependencies[defaults: .standard, key: .lastOpen] else {
|
||||||
_timeSinceLastOpen = .greatestFiniteMagnitude
|
_timeSinceLastOpen = .greatestFiniteMagnitude
|
||||||
return .greatestFiniteMagnitude
|
return .greatestFiniteMagnitude
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ extension MessageReceiver {
|
||||||
guard threadVariant == .contact else { return }
|
guard threadVariant == .contact else { return }
|
||||||
|
|
||||||
switch message.kind {
|
switch message.kind {
|
||||||
case .preOffer: try MessageReceiver.handleNewCallMessage(db, message: message)
|
case .preOffer: try MessageReceiver.handleNewCallMessage(db, message: message, using: dependencies)
|
||||||
case .offer: MessageReceiver.handleOfferCallMessage(db, message: message)
|
case .offer: MessageReceiver.handleOfferCallMessage(db, message: message)
|
||||||
case .answer: MessageReceiver.handleAnswerCallMessage(db, message: message, using: dependencies)
|
case .answer: MessageReceiver.handleAnswerCallMessage(db, message: message, using: dependencies)
|
||||||
case .provisionalAnswer: break // TODO: Implement
|
case .provisionalAnswer: break // TODO: Implement
|
||||||
|
@ -44,12 +44,16 @@ extension MessageReceiver {
|
||||||
|
|
||||||
// MARK: - Specific Handling
|
// MARK: - Specific Handling
|
||||||
|
|
||||||
private static func handleNewCallMessage(_ db: Database, message: CallMessage) throws {
|
private static func handleNewCallMessage(
|
||||||
|
_ db: Database,
|
||||||
|
message: CallMessage,
|
||||||
|
using dependencies: Dependencies
|
||||||
|
) throws {
|
||||||
SNLog("[Calls] Received pre-offer message.")
|
SNLog("[Calls] Received pre-offer message.")
|
||||||
|
|
||||||
// Determine whether the app is active based on the prefs rather than the UIApplication state to avoid
|
// Determine whether the app is active based on the prefs rather than the UIApplication state to avoid
|
||||||
// requiring main-thread execution
|
// requiring main-thread execution
|
||||||
let isMainAppActive: Bool = (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false)
|
let isMainAppActive: Bool = dependencies[defaults: .appGroup, key: .isMainAppActive]
|
||||||
|
|
||||||
// It is enough just ignoring the pre offers, other call messages
|
// It is enough just ignoring the pre offers, other call messages
|
||||||
// for this call would be dropped because of no Session call instance
|
// for this call would be dropped because of no Session call instance
|
||||||
|
|
|
@ -21,7 +21,7 @@ extension MessageReceiver {
|
||||||
// Note: `message.sentTimestamp` is in ms (convert to TimeInterval before converting to
|
// Note: `message.sentTimestamp` is in ms (convert to TimeInterval before converting to
|
||||||
// seconds to maintain the accuracy)
|
// seconds to maintain the accuracy)
|
||||||
let messageSentTimestamp: TimeInterval = (TimeInterval(message.sentTimestamp ?? 0) / 1000)
|
let messageSentTimestamp: TimeInterval = (TimeInterval(message.sentTimestamp ?? 0) / 1000)
|
||||||
let isMainAppActive: Bool = (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false)
|
let isMainAppActive: Bool = dependencies[defaults: .appGroup, key: .isMainAppActive]
|
||||||
let expiresInSeconds: TimeInterval? = proto.hasExpirationTimer ? TimeInterval(proto.expirationTimer) : nil
|
let expiresInSeconds: TimeInterval? = proto.hasExpirationTimer ? TimeInterval(proto.expirationTimer) : nil
|
||||||
|
|
||||||
// Update profile if needed (want to do this regardless of whether the message exists or
|
// Update profile if needed (want to do this regardless of whether the message exists or
|
||||||
|
@ -400,7 +400,7 @@ extension MessageReceiver {
|
||||||
case .react:
|
case .react:
|
||||||
// Determine whether the app is active based on the prefs rather than the UIApplication state to avoid
|
// Determine whether the app is active based on the prefs rather than the UIApplication state to avoid
|
||||||
// requiring main-thread execution
|
// requiring main-thread execution
|
||||||
let isMainAppActive: Bool = (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false)
|
let isMainAppActive: Bool = dependencies[defaults: .appGroup, key: .isMainAppActive]
|
||||||
let timestampMs: Int64 = Int64(messageSentTimestamp * 1000)
|
let timestampMs: Int64 = Int64(messageSentTimestamp * 1000)
|
||||||
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db)
|
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||||
let reaction: Reaction = try Reaction(
|
let reaction: Reaction = try Reaction(
|
||||||
|
|
|
@ -303,7 +303,7 @@ public final class MessageSender {
|
||||||
guard
|
guard
|
||||||
let job: Job = job,
|
let job: Job = job,
|
||||||
shouldNotify &&
|
shouldNotify &&
|
||||||
UserDefaults.sharedLokiProject?[.isMainAppActive] != true
|
!dependencies[defaults: .appGroup, key: .isMainAppActive]
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
NotifyPushServerJob.run(
|
NotifyPushServerJob.run(
|
||||||
|
|
|
@ -30,12 +30,6 @@ extension PushNotificationAPI {
|
||||||
/// The message's swarm expiry timestamp (unix epoch milliseconds)
|
/// The message's swarm expiry timestamp (unix epoch milliseconds)
|
||||||
public let expirationTimestampMs: Int64
|
public let expirationTimestampMs: Int64
|
||||||
|
|
||||||
/// The swarm timestamp when the message was created (unix epoch milliseconds)
|
|
||||||
public let createdTimestampMs: Int64
|
|
||||||
|
|
||||||
/// The message's swarm expiry timestamp (unix epoch milliseconds)
|
|
||||||
public let expirationTimestampMs: Int64
|
|
||||||
|
|
||||||
/// The length of the message data. This is always included, even if the message content
|
/// 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.
|
/// itself was too large to fit into the push notification.
|
||||||
public let dataLength: Int
|
public let dataLength: Int
|
||||||
|
|
|
@ -30,9 +30,9 @@ public enum PushNotificationAPI {
|
||||||
HTTP.PreparedRequest<PushNotificationAPI.LegacyPushServerResponse>?
|
HTTP.PreparedRequest<PushNotificationAPI.LegacyPushServerResponse>?
|
||||||
)
|
)
|
||||||
let hexEncodedToken: String = token.toHexString()
|
let hexEncodedToken: String = token.toHexString()
|
||||||
let oldToken: String? = dependencies[singleton: .standardUserDefaults][.deviceToken]
|
let oldToken: String? = dependencies[defaults: .standard, key: .deviceToken]
|
||||||
let lastUploadTime: Double = dependencies[singleton: .standardUserDefaults][.lastDeviceTokenUpload]
|
let lastUploadTime: Double = dependencies[defaults: .standard, key: .lastDeviceTokenUpload]
|
||||||
let now: TimeInterval = Date().timeIntervalSince1970
|
let now: TimeInterval = dependencies.dateNow.timeIntervalSince1970
|
||||||
|
|
||||||
guard isForcedUpdate || hexEncodedToken != oldToken || now - lastUploadTime > tokenExpirationInterval else {
|
guard isForcedUpdate || hexEncodedToken != oldToken || now - lastUploadTime > tokenExpirationInterval else {
|
||||||
SNLog("Device token hasn't changed or expired; no need to re-upload.")
|
SNLog("Device token hasn't changed or expired; no need to re-upload.")
|
||||||
|
@ -54,9 +54,9 @@ public enum PushNotificationAPI {
|
||||||
receiveOutput: { _, response in
|
receiveOutput: { _, response in
|
||||||
guard response.success == true else { return }
|
guard response.success == true else { return }
|
||||||
|
|
||||||
dependencies[singleton: .standardUserDefaults][.deviceToken] = hexEncodedToken
|
dependencies[defaults: .standard, key: .deviceToken] = hexEncodedToken
|
||||||
dependencies[singleton: .standardUserDefaults][.lastDeviceTokenUpload] = now
|
dependencies[defaults: .standard, key: .lastDeviceTokenUpload] = now
|
||||||
dependencies[singleton: .standardUserDefaults][.isUsingFullAPNs] = true
|
dependencies[defaults: .standard, key: .isUsingFullAPNs] = true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let preparedLegacyGroupRequest = try PushNotificationAPI
|
let preparedLegacyGroupRequest = try PushNotificationAPI
|
||||||
|
@ -131,7 +131,7 @@ public enum PushNotificationAPI {
|
||||||
receiveOutput: { _, response in
|
receiveOutput: { _, response in
|
||||||
guard response.success == true else { return }
|
guard response.success == true else { return }
|
||||||
|
|
||||||
dependencies[singleton: .standardUserDefaults][.deviceToken] = nil
|
dependencies[defaults: .standard, key: .deviceToken] = nil
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -175,8 +175,8 @@ public enum PushNotificationAPI {
|
||||||
using dependencies: Dependencies = Dependencies()
|
using dependencies: Dependencies = Dependencies()
|
||||||
) throws -> HTTP.PreparedRequest<SubscribeResponse> {
|
) throws -> HTTP.PreparedRequest<SubscribeResponse> {
|
||||||
guard
|
guard
|
||||||
dependencies[singleton: .standardUserDefaults][.isUsingFullAPNs],
|
dependencies[defaults: .standard, key: .isUsingFullAPNs],
|
||||||
let token: String = dependencies[singleton: .standardUserDefaults][.deviceToken]
|
let token: String = dependencies[defaults: .standard, key: .deviceToken]
|
||||||
else { throw HTTPError.invalidRequest }
|
else { throw HTTPError.invalidRequest }
|
||||||
|
|
||||||
guard let notificationsEncryptionKey: Data = try? getOrGenerateEncryptionKey(using: dependencies) else {
|
guard let notificationsEncryptionKey: Data = try? getOrGenerateEncryptionKey(using: dependencies) else {
|
||||||
|
@ -312,13 +312,13 @@ public enum PushNotificationAPI {
|
||||||
legacyGroupIds: Set<String>,
|
legacyGroupIds: Set<String>,
|
||||||
using dependencies: Dependencies = Dependencies()
|
using dependencies: Dependencies = Dependencies()
|
||||||
) throws -> HTTP.PreparedRequest<LegacyPushServerResponse>? {
|
) throws -> HTTP.PreparedRequest<LegacyPushServerResponse>? {
|
||||||
let isUsingFullAPNs = dependencies[singleton: .standardUserDefaults][.isUsingFullAPNs]
|
let isUsingFullAPNs = dependencies[defaults: .standard, key: .isUsingFullAPNs]
|
||||||
|
|
||||||
// Only continue if PNs are enabled and we have a device token
|
// Only continue if PNs are enabled and we have a device token
|
||||||
guard
|
guard
|
||||||
!legacyGroupIds.isEmpty,
|
!legacyGroupIds.isEmpty,
|
||||||
(forced || isUsingFullAPNs),
|
(forced || isUsingFullAPNs),
|
||||||
let deviceToken: String = (token ?? dependencies[singleton: .standardUserDefaults][.deviceToken])
|
let deviceToken: String = (token ?? dependencies[defaults: .standard, key: .deviceToken])
|
||||||
else { return nil }
|
else { return nil }
|
||||||
|
|
||||||
return try PushNotificationAPI
|
return try PushNotificationAPI
|
||||||
|
|
|
@ -61,7 +61,7 @@ public final class CurrentUserPoller: Poller {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func handlePollError(_ error: Error, for publicKey: String, using dependencies: Dependencies) -> Bool {
|
override func handlePollError(_ error: Error, for publicKey: String, using dependencies: Dependencies) -> Bool {
|
||||||
if UserDefaults.sharedLokiProject?[.isMainAppActive] != true {
|
if !dependencies[defaults: .appGroup, key: .isMainAppActive] {
|
||||||
// Do nothing when an error gets throws right after returning from the background (happens frequently)
|
// Do nothing when an error gets throws right after returning from the background (happens frequently)
|
||||||
}
|
}
|
||||||
else if
|
else if
|
||||||
|
|
|
@ -170,7 +170,7 @@ extension OpenGroupAPI {
|
||||||
dependencies.mutate(cache: .openGroupManager) { cache in
|
dependencies.mutate(cache: .openGroupManager) { cache in
|
||||||
cache.hasPerformedInitialPoll[server] = true
|
cache.hasPerformedInitialPoll[server] = true
|
||||||
cache.timeSinceLastPoll[server] = dependencies.dateNow.timeIntervalSince1970
|
cache.timeSinceLastPoll[server] = dependencies.dateNow.timeIntervalSince1970
|
||||||
dependencies[singleton: .standardUserDefaults][.lastOpen] = dependencies.dateNow
|
dependencies[defaults: .standard, key: .lastOpen] = dependencies.dateNow
|
||||||
}
|
}
|
||||||
|
|
||||||
SNLog("Open group polling finished for \(server).")
|
SNLog("Open group polling finished for \(server).")
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class TypingIndicators {
|
||||||
//
|
//
|
||||||
// We also don't want to show/send typing indicators for message requests
|
// We also don't want to show/send typing indicators for message requests
|
||||||
guard
|
guard
|
||||||
dependencies[singleton: .storage][.typingIndicatorsEnabled] &&
|
dependencies[singleton: .storage, key: .typingIndicatorsEnabled] &&
|
||||||
!threadIsBlocked &&
|
!threadIsBlocked &&
|
||||||
!threadIsMessageRequest
|
!threadIsMessageRequest
|
||||||
else { return nil }
|
else { return nil }
|
||||||
|
|
|
@ -374,7 +374,8 @@ public struct ProfileManager {
|
||||||
try success?(db)
|
try success?(db)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
failure: failure
|
failure: failure,
|
||||||
|
using: dependencies
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -383,7 +384,8 @@ public struct ProfileManager {
|
||||||
queue: DispatchQueue,
|
queue: DispatchQueue,
|
||||||
imageData: Data,
|
imageData: Data,
|
||||||
success: @escaping ((downloadUrl: String, fileName: String, profileKey: Data)) -> (),
|
success: @escaping ((downloadUrl: String, fileName: String, profileKey: Data)) -> (),
|
||||||
failure: ((ProfileManagerError) -> ())? = nil
|
failure: ((ProfileManagerError) -> ())? = nil,
|
||||||
|
using dependencies: Dependencies
|
||||||
) {
|
) {
|
||||||
queue.async {
|
queue.async {
|
||||||
// If the profile avatar was updated or removed then encrypt with a new profile key
|
// If the profile avatar was updated or removed then encrypt with a new profile key
|
||||||
|
@ -504,7 +506,7 @@ public struct ProfileManager {
|
||||||
|
|
||||||
// Update the cached avatar image value
|
// Update the cached avatar image value
|
||||||
profileAvatarCache.mutate { $0[fileName] = avatarImageData }
|
profileAvatarCache.mutate { $0[fileName] = avatarImageData }
|
||||||
UserDefaults.standard[.lastProfilePictureUpload] = Date()
|
dependencies[defaults: .standard, key: .lastProfilePictureUpload] = dependencies.dateNow
|
||||||
|
|
||||||
SNLog("Successfully uploaded avatar image.")
|
SNLog("Successfully uploaded avatar image.")
|
||||||
success((downloadUrl, fileName, newProfileKey))
|
success((downloadUrl, fileName, newProfileKey))
|
||||||
|
|
|
@ -63,7 +63,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
||||||
dependencies[singleton: .storage] = mockStorage
|
dependencies[singleton: .storage] = mockStorage
|
||||||
dependencies[singleton: .network] = mockNetwork
|
dependencies[singleton: .network] = mockNetwork
|
||||||
dependencies[singleton: .crypto] = mockCrypto
|
dependencies[singleton: .crypto] = mockCrypto
|
||||||
dependencies[singleton: .standardUserDefaults] = mockUserDefaults
|
dependencies[defaults: .standard] = mockUserDefaults
|
||||||
dependencies[cache: .openGroupManager] = mockOGMCache
|
dependencies[cache: .openGroupManager] = mockOGMCache
|
||||||
|
|
||||||
testInteraction1 = Interaction(
|
testInteraction1 = Interaction(
|
||||||
|
@ -252,7 +252,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
||||||
it("defaults the time since last open to greatestFiniteMagnitude") {
|
it("defaults the time since last open to greatestFiniteMagnitude") {
|
||||||
mockUserDefaults
|
mockUserDefaults
|
||||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||||
}
|
}
|
||||||
.thenReturn(nil)
|
.thenReturn(nil)
|
||||||
|
|
||||||
|
@ -264,7 +264,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
||||||
it("returns the time since the last open") {
|
it("returns the time since the last open") {
|
||||||
mockUserDefaults
|
mockUserDefaults
|
||||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||||
}
|
}
|
||||||
.thenReturn(Date(timeIntervalSince1970: 1234567880))
|
.thenReturn(Date(timeIntervalSince1970: 1234567880))
|
||||||
dependencies.dateNow = Date(timeIntervalSince1970: 1234567890)
|
dependencies.dateNow = Date(timeIntervalSince1970: 1234567890)
|
||||||
|
@ -277,7 +277,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
||||||
it("caches the time since the last open") {
|
it("caches the time since the last open") {
|
||||||
mockUserDefaults
|
mockUserDefaults
|
||||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||||
}
|
}
|
||||||
.thenReturn(Date(timeIntervalSince1970: 1234567770))
|
.thenReturn(Date(timeIntervalSince1970: 1234567770))
|
||||||
dependencies.dateNow = Date(timeIntervalSince1970: 1234567780)
|
dependencies.dateNow = Date(timeIntervalSince1970: 1234567780)
|
||||||
|
@ -287,7 +287,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
||||||
|
|
||||||
mockUserDefaults
|
mockUserDefaults
|
||||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||||
}
|
}
|
||||||
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
||||||
|
|
||||||
mockUserDefaults
|
mockUserDefaults
|
||||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||||
}
|
}
|
||||||
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
||||||
}
|
}
|
||||||
|
@ -783,7 +783,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
||||||
|
|
||||||
mockUserDefaults
|
mockUserDefaults
|
||||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||||
}
|
}
|
||||||
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
||||||
}
|
}
|
||||||
|
@ -928,7 +928,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
||||||
|
|
||||||
mockUserDefaults
|
mockUserDefaults
|
||||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||||
}
|
}
|
||||||
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
||||||
}
|
}
|
||||||
|
@ -1209,7 +1209,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
||||||
|
|
||||||
mockUserDefaults
|
mockUserDefaults
|
||||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||||
}
|
}
|
||||||
.thenReturn(nil)
|
.thenReturn(nil)
|
||||||
}
|
}
|
||||||
|
@ -3245,7 +3245,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
||||||
.to(call(matchingParameters: .all) {
|
.to(call(matchingParameters: .all) {
|
||||||
$0.set(
|
$0.set(
|
||||||
testDate,
|
testDate,
|
||||||
forKey: SNUserDefaults.Date.lastOpenGroupImageUpdate.rawValue
|
forKey: UserDefaultsInfo.DateKey.lastOpenGroupImageUpdate.rawValue
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
expect(
|
expect(
|
||||||
|
@ -3354,7 +3354,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
||||||
.toNot(call(matchingParameters: .all) {
|
.toNot(call(matchingParameters: .all) {
|
||||||
$0.set(
|
$0.set(
|
||||||
dependencies.dateNow,
|
dependencies.dateNow,
|
||||||
forKey: SNUserDefaults.Date.lastOpenGroupImageUpdate.rawValue
|
forKey: UserDefaultsInfo.DateKey.lastOpenGroupImageUpdate.rawValue
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -3436,7 +3436,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
||||||
.to(call(matchingParameters: .all) {
|
.to(call(matchingParameters: .all) {
|
||||||
$0.set(
|
$0.set(
|
||||||
dependencies.dateNow,
|
dependencies.dateNow,
|
||||||
forKey: SNUserDefaults.Date.lastOpenGroupImageUpdate.rawValue
|
forKey: UserDefaultsInfo.DateKey.lastOpenGroupImageUpdate.rawValue
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abort if the main app is running
|
// Abort if the main app is running
|
||||||
guard !(UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
|
guard !dependencies[defaults: .appGroup, key: .isMainAppActive] else {
|
||||||
return self.completeSilenty(using: dependencies)
|
return self.completeSilenty(using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,9 +46,8 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
|
||||||
SetCurrentAppContext(NotificationServiceExtensionContext())
|
SetCurrentAppContext(NotificationServiceExtensionContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
let isCallOngoing: Bool = (UserDefaults.sharedLokiProject?[.isCallOngoing])
|
let isCallOngoing: Bool = dependencies[defaults: .appGroup, key: .isCallOngoing]
|
||||||
.defaulting(to: false)
|
let lastCallPreOffer: Date? = dependencies[defaults: .appGroup, key: .lastCallPreOffer]
|
||||||
let lastCallPreOffer: Date? = UserDefaults.sharedLokiProject?[.lastCallPreOffer]
|
|
||||||
|
|
||||||
// Perform main setup
|
// Perform main setup
|
||||||
Storage.resumeDatabaseAccess(using: dependencies)
|
Storage.resumeDatabaseAccess(using: dependencies)
|
||||||
|
@ -240,8 +239,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
|
||||||
NSLog("[NotificationServiceExtension] Performing setup")
|
NSLog("[NotificationServiceExtension] Performing setup")
|
||||||
didPerformSetup = true
|
didPerformSetup = true
|
||||||
|
|
||||||
_ = AppVersion.sharedInstance()
|
AppVersion.configure(using: dependencies)
|
||||||
|
|
||||||
Cryptography.seedRandom()
|
Cryptography.seedRandom()
|
||||||
|
|
||||||
AppSetup.setupEnvironment(
|
AppSetup.setupEnvironment(
|
||||||
|
@ -264,7 +262,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
|
||||||
// this path should never occur. However, the service does have our push token
|
// this path should never occur. However, the service does have our push token
|
||||||
// so it is possible that could change in the future. If it does, do nothing
|
// so it is possible that could change in the future. If it does, do nothing
|
||||||
// and don't disturb the user. Messages will be processed when they open the app.
|
// and don't disturb the user. Messages will be processed when they open the app.
|
||||||
guard dependencies[singleton: .storage][.isReadyForAppExtensions] else {
|
guard dependencies[singleton: .storage, key: .isReadyForAppExtensions] else {
|
||||||
NSLog("[NotificationServiceExtension] Not ready for extensions")
|
NSLog("[NotificationServiceExtension] Not ready for extensions")
|
||||||
self?.completeSilenty(using: dependencies)
|
self?.completeSilenty(using: dependencies)
|
||||||
return
|
return
|
||||||
|
@ -368,7 +366,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
NSLog("[NotificationServiceExtension] Successfully notified main app of call message.")
|
NSLog("[NotificationServiceExtension] Successfully notified main app of call message.")
|
||||||
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = Date()
|
dependencies[defaults: .appGroup, key: .lastCallPreOffer] = Date()
|
||||||
self.completeSilenty(using: dependencies)
|
self.completeSilenty(using: dependencies)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,13 +46,6 @@ final class NotificationServiceExtensionContext : NSObject, AppContext {
|
||||||
return groupContainerURL.path
|
return groupContainerURL.path
|
||||||
}
|
}
|
||||||
|
|
||||||
func appUserDefaults() -> UserDefaults {
|
|
||||||
guard let userDefaults = UserDefaults.sharedLokiProject else {
|
|
||||||
preconditionFailure("Couldn't set up shared user defaults.")
|
|
||||||
}
|
|
||||||
return userDefaults
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Currently Unused
|
// MARK: - Currently Unused
|
||||||
|
|
||||||
let frame = CGRect.zero
|
let frame = CGRect.zero
|
||||||
|
|
|
@ -9,5 +9,4 @@
|
||||||
#import <SessionUtilitiesKit/UIView+OWS.h>
|
#import <SessionUtilitiesKit/UIView+OWS.h>
|
||||||
#import <SessionUtilitiesKit/AppContext.h>
|
#import <SessionUtilitiesKit/AppContext.h>
|
||||||
#import <SessionMessagingKit/AppReadiness.h>
|
#import <SessionMessagingKit/AppReadiness.h>
|
||||||
#import <SignalUtilitiesKit/AppVersion.h>
|
|
||||||
#import <SessionUtilitiesKit/OWSMath.h>
|
#import <SessionUtilitiesKit/OWSMath.h>
|
||||||
|
|
|
@ -164,12 +164,6 @@ final class ShareAppExtensionContext: NSObject, AppContext {
|
||||||
return (targetPath ?? "")
|
return (targetPath ?? "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func appUserDefaults() -> UserDefaults {
|
|
||||||
owsAssertDebug(UserDefaults.sharedLokiProject != nil)
|
|
||||||
|
|
||||||
return (UserDefaults.sharedLokiProject ?? UserDefaults.standard)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setStatusBarHidden(_ isHidden: Bool, animated isAnimated: Bool) {
|
func setStatusBarHidden(_ isHidden: Bool, animated isAnimated: Bool) {
|
||||||
OWSLogger.info("Ignoring request to show/hide status bar since we're in an app extension")
|
OWSLogger.info("Ignoring request to show/hide status bar since we're in an app extension")
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,9 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
|
||||||
override func loadView() {
|
override func loadView() {
|
||||||
super.loadView()
|
super.loadView()
|
||||||
|
|
||||||
|
// Called via the OS so create a default 'Dependencies' instance
|
||||||
|
let dependencies: Dependencies = Dependencies()
|
||||||
|
|
||||||
view.themeBackgroundColor = .backgroundPrimary
|
view.themeBackgroundColor = .backgroundPrimary
|
||||||
|
|
||||||
// This should be the first thing we do (Note: If you leave the share context and return to it
|
// This should be the first thing we do (Note: If you leave the share context and return to it
|
||||||
|
@ -38,7 +41,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
|
||||||
|
|
||||||
Logger.info("")
|
Logger.info("")
|
||||||
|
|
||||||
_ = AppVersion.sharedInstance()
|
AppVersion.configure(using: dependencies)
|
||||||
|
|
||||||
Cryptography.seedRandom()
|
Cryptography.seedRandom()
|
||||||
|
|
||||||
|
@ -51,9 +54,6 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called via the OS so create a default 'Dependencies' instance
|
|
||||||
let dependencies: Dependencies = Dependencies()
|
|
||||||
|
|
||||||
AppSetup.setupEnvironment(
|
AppSetup.setupEnvironment(
|
||||||
appSpecificBlock: {
|
appSpecificBlock: {
|
||||||
Environment.shared?.notificationsManager.mutate {
|
Environment.shared?.notificationsManager.mutate {
|
||||||
|
@ -158,7 +158,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
|
||||||
// We don't need to use SyncPushTokensJob in the SAE.
|
// We don't need to use SyncPushTokensJob in the SAE.
|
||||||
// We don't need to use DeviceSleepManager in the SAE.
|
// We don't need to use DeviceSleepManager in the SAE.
|
||||||
|
|
||||||
AppVersion.sharedInstance().saeLaunchDidComplete()
|
AppVersion.shared.saeLaunchDidComplete(using: dependencies)
|
||||||
|
|
||||||
showLockScreenOrMainContent(using: dependencies)
|
showLockScreenOrMainContent(using: dependencies)
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
|
||||||
// Called via the OS so create a default 'Dependencies' instance
|
// Called via the OS so create a default 'Dependencies' instance
|
||||||
let dependencies: Dependencies = Dependencies()
|
let dependencies: Dependencies = Dependencies()
|
||||||
|
|
||||||
if dependencies[singleton: .storage][.isScreenLockEnabled] {
|
if dependencies[singleton: .storage, key: .isScreenLockEnabled] {
|
||||||
self.dismiss(animated: false) { [weak self] in
|
self.dismiss(animated: false) { [weak self] in
|
||||||
AssertIsOnMainThread()
|
AssertIsOnMainThread()
|
||||||
self?.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
self?.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
||||||
|
@ -210,7 +210,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
|
||||||
private func showLockScreenOrMainContent(
|
private func showLockScreenOrMainContent(
|
||||||
using dependencies: Dependencies
|
using dependencies: Dependencies
|
||||||
) {
|
) {
|
||||||
if dependencies[singleton: .storage][.isScreenLockEnabled] {
|
if dependencies[singleton: .storage, key: .isScreenLockEnabled] {
|
||||||
showLockScreen()
|
showLockScreen()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -26,8 +26,8 @@ public final class SnodeAPI {
|
||||||
|
|
||||||
// MARK: - Hardfork version
|
// MARK: - Hardfork version
|
||||||
|
|
||||||
public static var hardfork = UserDefaults.standard[.hardfork]
|
public static var hardfork: Int = Dependencies()[defaults: .standard, key: .hardfork]
|
||||||
public static var softfork = UserDefaults.standard[.softfork]
|
public static var softfork: Int = Dependencies()[defaults: .standard, key: .softfork]
|
||||||
|
|
||||||
// MARK: - Settings
|
// MARK: - Settings
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ public final class SnodeAPI {
|
||||||
loadSnodePoolIfNeeded(using: dependencies)
|
loadSnodePoolIfNeeded(using: dependencies)
|
||||||
|
|
||||||
let now: Date = Date()
|
let now: Date = Date()
|
||||||
let hasSnodePoolExpired: Bool = dependencies[singleton: .storage][.lastSnodePoolRefreshDate]
|
let hasSnodePoolExpired: Bool = dependencies[singleton: .storage, key: .lastSnodePoolRefreshDate]
|
||||||
.map { now.timeIntervalSince($0) > 2 * 60 * 60 }
|
.map { now.timeIntervalSince($0) > 2 * 60 * 60 }
|
||||||
.defaulting(to: true)
|
.defaulting(to: true)
|
||||||
let snodePool: Set<Snode> = SnodeAPI.snodePool.wrappedValue
|
let snodePool: Set<Snode> = SnodeAPI.snodePool.wrappedValue
|
||||||
|
@ -1199,14 +1199,14 @@ public final class SnodeAPI {
|
||||||
|
|
||||||
if snodeResponse.hardFork[1] > softfork {
|
if snodeResponse.hardFork[1] > softfork {
|
||||||
softfork = snodeResponse.hardFork[1]
|
softfork = snodeResponse.hardFork[1]
|
||||||
UserDefaults.standard[.softfork] = softfork
|
dependencies[defaults: .standard, key: .softfork] = softfork
|
||||||
}
|
}
|
||||||
|
|
||||||
if snodeResponse.hardFork[0] > hardfork {
|
if snodeResponse.hardFork[0] > hardfork {
|
||||||
hardfork = snodeResponse.hardFork[0]
|
hardfork = snodeResponse.hardFork[0]
|
||||||
UserDefaults.standard[.hardfork] = hardfork
|
dependencies[defaults: .standard, key: .hardfork] = hardfork
|
||||||
softfork = snodeResponse.hardFork[1]
|
softfork = snodeResponse.hardFork[1]
|
||||||
UserDefaults.standard[.softfork] = softfork
|
dependencies[defaults: .standard, key: .softfork] = softfork
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class TopBannerController: UIViewController {
|
||||||
)
|
)
|
||||||
result.contentMode = .center
|
result.contentMode = .center
|
||||||
result.themeTintColor = .black
|
result.themeTintColor = .black
|
||||||
result.addTarget(self, action: #selector(dismissBanner), for: .touchUpInside)
|
result.addTarget(self, action: #selector(dismissBannerTapped), for: .touchUpInside)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
@ -136,9 +136,11 @@ public class TopBannerController: UIViewController {
|
||||||
|
|
||||||
// MARK: - Actions
|
// MARK: - Actions
|
||||||
|
|
||||||
@objc private func dismissBanner() {
|
@objc private func dismissBannerTapped() { dismissBanner() }
|
||||||
|
|
||||||
|
private func dismissBanner(using dependencies: Dependencies = Dependencies()) {
|
||||||
// Remove the cached warning
|
// Remove the cached warning
|
||||||
UserDefaults.sharedLokiProject?[.topBannerWarningToShow] = nil
|
dependencies[defaults: .appGroup, key: .topBannerWarningToShow] = nil
|
||||||
|
|
||||||
UIView.animate(
|
UIView.animate(
|
||||||
withDuration: 0.3,
|
withDuration: 0.3,
|
||||||
|
@ -155,10 +157,14 @@ public class TopBannerController: UIViewController {
|
||||||
|
|
||||||
// MARK: - Functions
|
// MARK: - Functions
|
||||||
|
|
||||||
public static func show(warning: Warning, inWindowFor view: UIView? = nil) {
|
public static func show(
|
||||||
|
warning: Warning,
|
||||||
|
inWindowFor view: UIView? = nil,
|
||||||
|
using dependencies: Dependencies = Dependencies()
|
||||||
|
) {
|
||||||
guard Thread.isMainThread else {
|
guard Thread.isMainThread else {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
TopBannerController.show(warning: warning, inWindowFor: view)
|
TopBannerController.show(warning: warning, inWindowFor: view, using: dependencies)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -169,7 +175,7 @@ public class TopBannerController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache the banner to show (so we can show it on re-launch)
|
// Cache the banner to show (so we can show it on re-launch)
|
||||||
UserDefaults.sharedLokiProject?[.topBannerWarningToShow] = warning.rawValue
|
dependencies[defaults: .appGroup, key: .topBannerWarningToShow] = warning.rawValue
|
||||||
|
|
||||||
UIView.performWithoutAnimation {
|
UIView.performWithoutAnimation {
|
||||||
instance.bannerLabel.text = warning.text
|
instance.bannerLabel.text = warning.text
|
||||||
|
|
|
@ -36,7 +36,7 @@ public enum ThemeManager {
|
||||||
internal static var dependencies: Dependencies = Dependencies()
|
internal static var dependencies: Dependencies = Dependencies()
|
||||||
|
|
||||||
public static var currentTheme: Theme = {
|
public static var currentTheme: Theme = {
|
||||||
(_initialTheme ?? dependencies[singleton: .storage][.theme].defaulting(to: Theme.classicDark))
|
(_initialTheme ?? dependencies[singleton: .storage, key: .theme].defaulting(to: Theme.classicDark))
|
||||||
}() {
|
}() {
|
||||||
didSet {
|
didSet {
|
||||||
// Only update if it was changed
|
// Only update if it was changed
|
||||||
|
@ -60,7 +60,7 @@ public enum ThemeManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var primaryColor: Theme.PrimaryColor = {
|
public static var primaryColor: Theme.PrimaryColor = {
|
||||||
(_initialPrimaryColor ?? dependencies[singleton: .storage][.themePrimaryColor].defaulting(to: Theme.PrimaryColor.green))
|
(_initialPrimaryColor ?? dependencies[singleton: .storage, key: .themePrimaryColor].defaulting(to: Theme.PrimaryColor.green))
|
||||||
}() {
|
}() {
|
||||||
didSet {
|
didSet {
|
||||||
// Only update if it was changed
|
// Only update if it was changed
|
||||||
|
@ -75,7 +75,7 @@ public enum ThemeManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var matchSystemNightModeSetting: Bool = {
|
public static var matchSystemNightModeSetting: Bool = {
|
||||||
(_initialMatchSystemNightModeSetting ?? dependencies[singleton: .storage][.themeMatchSystemDayNightCycle])
|
(_initialMatchSystemNightModeSetting ?? dependencies[singleton: .storage, key: .themeMatchSystemDayNightCycle])
|
||||||
}() {
|
}() {
|
||||||
didSet {
|
didSet {
|
||||||
// Only update if it was changed
|
// Only update if it was changed
|
||||||
|
@ -134,8 +134,8 @@ public enum ThemeManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func applySavedTheme(using dependencies: Dependencies) {
|
public static func applySavedTheme(using dependencies: Dependencies) {
|
||||||
ThemeManager.primaryColor = dependencies[singleton: .storage][.themePrimaryColor].defaulting(to: Theme.PrimaryColor.green)
|
ThemeManager.primaryColor = dependencies[singleton: .storage, key: .themePrimaryColor].defaulting(to: Theme.PrimaryColor.green)
|
||||||
ThemeManager.currentTheme = dependencies[singleton: .storage][.theme].defaulting(to: Theme.classicDark)
|
ThemeManager.currentTheme = dependencies[singleton: .storage, key: .theme].defaulting(to: Theme.classicDark)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func applyNavigationStyling() {
|
public static func applyNavigationStyling() {
|
||||||
|
|
|
@ -150,31 +150,7 @@ public protocol EnumStringSetting: RawRepresentable where RawValue == String {}
|
||||||
|
|
||||||
// MARK: - GRDB Interactions
|
// MARK: - GRDB Interactions
|
||||||
|
|
||||||
public extension Storage {
|
|
||||||
subscript(key: Setting.BoolKey) -> Bool {
|
|
||||||
// Default to false if it doesn't exist
|
|
||||||
return (read { db in db[key] } ?? false)
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript(key: Setting.DoubleKey) -> Double? { return read { db in db[key] } }
|
|
||||||
subscript(key: Setting.IntKey) -> Int? { return read { db in db[key] } }
|
|
||||||
subscript(key: Setting.StringKey) -> String? { return read { db in db[key] } }
|
|
||||||
subscript(key: Setting.DateKey) -> Date? { return read { db in db[key] } }
|
|
||||||
|
|
||||||
subscript<T: EnumIntSetting>(key: Setting.EnumKey) -> T? { return read { db in db[key] } }
|
|
||||||
subscript<T: EnumStringSetting>(key: Setting.EnumKey) -> T? { return read { db in db[key] } }
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension Database {
|
public extension Database {
|
||||||
@discardableResult func unsafeSet<T: Numeric>(key: String, value: T?) -> Setting? {
|
|
||||||
guard let value: T = value else {
|
|
||||||
_ = try? Setting.filter(id: key).deleteAll(self)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return try? Setting(key: key, value: value)?.saved(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
private subscript(key: String) -> Setting? {
|
private subscript(key: String) -> Setting? {
|
||||||
get { try? Setting.filter(id: key).fetchOne(self) }
|
get { try? Setting.filter(id: key).fetchOne(self) }
|
||||||
set {
|
set {
|
||||||
|
|
|
@ -8,6 +8,7 @@ public class Dependencies {
|
||||||
|
|
||||||
private static var singletonInstances: Atomic<[Int: Any]> = Atomic([:])
|
private static var singletonInstances: Atomic<[Int: Any]> = Atomic([:])
|
||||||
private static var cacheInstances: Atomic<[Int: MutableCacheType]> = Atomic([:])
|
private static var cacheInstances: Atomic<[Int: MutableCacheType]> = Atomic([:])
|
||||||
|
private static var userDefaultsInstances: Atomic<[Int: (any UserDefaultsType)]> = Atomic([:])
|
||||||
|
|
||||||
// MARK: - Subscript Access
|
// MARK: - Subscript Access
|
||||||
|
|
||||||
|
@ -19,6 +20,9 @@ public class Dependencies {
|
||||||
getValueSettingIfNull(cache: cache, &Dependencies.cacheInstances)
|
getValueSettingIfNull(cache: cache, &Dependencies.cacheInstances)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public subscript<U: UserDefaultsType>(defaults defaults: UserDefaultsInfo.Config<U>) -> U {
|
||||||
|
getValueSettingIfNull(defaults: defaults, &Dependencies.userDefaultsInstances)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Timing and Async Handling
|
// MARK: - Timing and Async Handling
|
||||||
|
|
||||||
|
@ -82,4 +86,80 @@ public class Dependencies {
|
||||||
|
|
||||||
return cache.immutableInstance(value)
|
return cache.immutableInstance(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@discardableResult private func getValueSettingIfNull<U: UserDefaultsType>(
|
||||||
|
defaults: UserDefaultsInfo.Config<U>,
|
||||||
|
_ store: inout Atomic<[Int: UserDefaultsType]>
|
||||||
|
) -> U {
|
||||||
|
guard let value: U = (store.wrappedValue[defaults.key] as? U) else {
|
||||||
|
let value: U = defaults.createInstance(self)
|
||||||
|
store.mutate { $0[defaults.key] = value }
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Storage Setting Convenience
|
||||||
|
|
||||||
|
public extension Dependencies {
|
||||||
|
subscript(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.BoolKey) -> Bool {
|
||||||
|
return self[singleton: singleton]
|
||||||
|
.read { db in db[key] }
|
||||||
|
.defaulting(to: false) // Default to false if it doesn't exist
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.DoubleKey) -> Double? {
|
||||||
|
return self[singleton: singleton].read { db in db[key] }
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.IntKey) -> Int? {
|
||||||
|
return self[singleton: singleton].read { db in db[key] }
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.StringKey) -> String? {
|
||||||
|
return self[singleton: singleton].read { db in db[key] }
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.DateKey) -> Date? {
|
||||||
|
return self[singleton: singleton].read { db in db[key] }
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript<T: EnumIntSetting>(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.EnumKey) -> T? {
|
||||||
|
return self[singleton: singleton].read { db in db[key] }
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript<T: EnumStringSetting>(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.EnumKey) -> T? {
|
||||||
|
return self[singleton: singleton].read { db in db[key] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - UserDefaults Convenience
|
||||||
|
|
||||||
|
public extension Dependencies {
|
||||||
|
subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>, key key: UserDefaultsInfo.BoolKey) -> Bool {
|
||||||
|
get { return self[defaults: defaults].bool(forKey: key.rawValue) }
|
||||||
|
set { self[defaults: defaults].set(newValue, forKey: key.rawValue) }
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>, key key: UserDefaultsInfo.DateKey) -> Date? {
|
||||||
|
get { return self[defaults: defaults].object(forKey: key.rawValue) as? Date }
|
||||||
|
set { self[defaults: defaults].set(newValue, forKey: key.rawValue) }
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>, key key: UserDefaultsInfo.DoubleKey) -> Double {
|
||||||
|
get { return self[defaults: defaults].double(forKey: key.rawValue) }
|
||||||
|
set { self[defaults: defaults].set(newValue, forKey: key.rawValue) }
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>, key key: UserDefaultsInfo.IntKey) -> Int {
|
||||||
|
get { return self[defaults: defaults].integer(forKey: key.rawValue) }
|
||||||
|
set { self[defaults: defaults].set(newValue, forKey: key.rawValue) }
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>, key key: UserDefaultsInfo.StringKey) -> String? {
|
||||||
|
get { return self[defaults: defaults].string(forKey: key.rawValue) }
|
||||||
|
set { self[defaults: defaults].set(newValue, forKey: key.rawValue) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// MARK: - UserDefaultsStorage
|
||||||
|
|
||||||
|
public class UserDefaultsStorage {}
|
||||||
|
|
||||||
|
public extension UserDefaultsStorage {
|
||||||
|
static var standard: UserDefaultsInfo.Config = UserDefaultsInfo.create(identifier: "standard") { _ in
|
||||||
|
UserDefaults.standard
|
||||||
|
}
|
||||||
|
static var appGroup: UserDefaultsInfo.Config = UserDefaultsInfo.create(identifier: UserDefaults.applicationGroup) { _ in
|
||||||
|
UserDefaults(suiteName: UserDefaults.applicationGroup)!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - UserDefaultsInfo
|
||||||
|
|
||||||
|
public enum UserDefaultsInfo {
|
||||||
|
public class Config<U: UserDefaultsType>: UserDefaultsStorage {
|
||||||
|
public let key: Int
|
||||||
|
public let createInstance: (Dependencies) -> U
|
||||||
|
|
||||||
|
fileprivate init(
|
||||||
|
identifier: String,
|
||||||
|
createInstance: @escaping (Dependencies) -> U
|
||||||
|
) {
|
||||||
|
self.key = identifier.hashValue
|
||||||
|
self.createInstance = createInstance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension UserDefaultsInfo {
|
||||||
|
static func create<U: UserDefaultsType>(
|
||||||
|
identifier: String,
|
||||||
|
createInstance: @escaping (Dependencies) -> U
|
||||||
|
) -> UserDefaultsInfo.Config<U> {
|
||||||
|
return UserDefaultsInfo.Config(
|
||||||
|
identifier: identifier,
|
||||||
|
createInstance: createInstance
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - UserDefaultsType
|
||||||
|
|
||||||
|
public protocol UserDefaultsType: AnyObject {
|
||||||
|
func object(forKey defaultName: String) -> Any?
|
||||||
|
func string(forKey defaultName: String) -> String?
|
||||||
|
func array(forKey defaultName: String) -> [Any]?
|
||||||
|
func dictionary(forKey defaultName: String) -> [String : Any]?
|
||||||
|
func data(forKey defaultName: String) -> Data?
|
||||||
|
func stringArray(forKey defaultName: String) -> [String]?
|
||||||
|
func integer(forKey defaultName: String) -> Int
|
||||||
|
func float(forKey defaultName: String) -> Float
|
||||||
|
func double(forKey defaultName: String) -> Double
|
||||||
|
func bool(forKey defaultName: String) -> Bool
|
||||||
|
func url(forKey defaultName: String) -> URL?
|
||||||
|
|
||||||
|
func set(_ value: Any?, forKey defaultName: String)
|
||||||
|
func set(_ value: Int, forKey defaultName: String)
|
||||||
|
func set(_ value: Float, forKey defaultName: String)
|
||||||
|
func set(_ value: Double, forKey defaultName: String)
|
||||||
|
func set(_ value: Bool, forKey defaultName: String)
|
||||||
|
func set(_ url: URL?, forKey defaultName: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UserDefaults: UserDefaultsType {}
|
||||||
|
|
||||||
|
public extension UserDefaults {
|
||||||
|
static let applicationGroup: String = "group.com.loki-project.loki-messenger"
|
||||||
|
|
||||||
|
static func removeAll() {
|
||||||
|
UserDefaultsStorage.standard.createInstance(Dependencies()).removeAll()
|
||||||
|
UserDefaultsStorage.appGroup.createInstance(Dependencies()).removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func removeAll() {
|
||||||
|
let data: [String: Any] = self.dictionaryRepresentation()
|
||||||
|
data.forEach { key, _ in self.removeObject(forKey: key) }
|
||||||
|
self.synchronize() // Shouldn't be needed but better safe than sorry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public extension UserDefaultsInfo {
|
||||||
|
struct BoolKey: RawRepresentable, ExpressibleByStringLiteral, Hashable {
|
||||||
|
public let rawValue: String
|
||||||
|
|
||||||
|
public init(_ rawValue: String) { self.rawValue = rawValue }
|
||||||
|
public init?(rawValue: String) { self.rawValue = rawValue }
|
||||||
|
public init(stringLiteral value: String) { self.init(value) }
|
||||||
|
public init(unicodeScalarLiteral value: String) { self.init(value) }
|
||||||
|
public init(extendedGraphemeClusterLiteral value: String) { self.init(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DateKey: RawRepresentable, ExpressibleByStringLiteral, Hashable {
|
||||||
|
public let rawValue: String
|
||||||
|
|
||||||
|
public init(_ rawValue: String) { self.rawValue = rawValue }
|
||||||
|
public init?(rawValue: String) { self.rawValue = rawValue }
|
||||||
|
public init(stringLiteral value: String) { self.init(value) }
|
||||||
|
public init(unicodeScalarLiteral value: String) { self.init(value) }
|
||||||
|
public init(extendedGraphemeClusterLiteral value: String) { self.init(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DoubleKey: RawRepresentable, ExpressibleByStringLiteral, Hashable {
|
||||||
|
public let rawValue: String
|
||||||
|
|
||||||
|
public init(_ rawValue: String) { self.rawValue = rawValue }
|
||||||
|
public init?(rawValue: String) { self.rawValue = rawValue }
|
||||||
|
public init(stringLiteral value: String) { self.init(value) }
|
||||||
|
public init(unicodeScalarLiteral value: String) { self.init(value) }
|
||||||
|
public init(extendedGraphemeClusterLiteral value: String) { self.init(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IntKey: RawRepresentable, ExpressibleByStringLiteral, Hashable {
|
||||||
|
public let rawValue: String
|
||||||
|
|
||||||
|
public init(_ rawValue: String) { self.rawValue = rawValue }
|
||||||
|
public init?(rawValue: String) { self.rawValue = rawValue }
|
||||||
|
public init(stringLiteral value: String) { self.init(value) }
|
||||||
|
public init(unicodeScalarLiteral value: String) { self.init(value) }
|
||||||
|
public init(extendedGraphemeClusterLiteral value: String) { self.init(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StringKey: RawRepresentable, ExpressibleByStringLiteral, Hashable {
|
||||||
|
public let rawValue: String
|
||||||
|
|
||||||
|
public init(_ rawValue: String) { self.rawValue = rawValue }
|
||||||
|
public init?(rawValue: String) { self.rawValue = rawValue }
|
||||||
|
public init(stringLiteral value: String) { self.init(value) }
|
||||||
|
public init(unicodeScalarLiteral value: String) { self.init(value) }
|
||||||
|
public init(extendedGraphemeClusterLiteral value: String) { self.init(value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - UserDefault Values
|
||||||
|
|
||||||
|
public extension UserDefaultsInfo.BoolKey {
|
||||||
|
/// Indicates whether the user has synced an initial config message from this device
|
||||||
|
static let hasSyncedInitialConfiguration: UserDefaultsInfo.BoolKey = "hasSyncedConfiguration"
|
||||||
|
|
||||||
|
/// Indicates whether the user has seen the suggestion to enable link previews
|
||||||
|
static let hasSeenLinkPreviewSuggestion: UserDefaultsInfo.BoolKey = "hasSeenLinkPreviewSuggestion"
|
||||||
|
|
||||||
|
/// Indicates whether the user has seen the IP exposure warning when enabling calls
|
||||||
|
///
|
||||||
|
/// **Note:** This is currently not in use (it was decided that it's better to warn the user every time they enable calls instead
|
||||||
|
/// of just the first time)
|
||||||
|
static let hasSeenCallIPExposureWarning: UserDefaultsInfo.BoolKey = "hasSeenCallIPExposureWarning"
|
||||||
|
|
||||||
|
/// Indicates whether the user has seen the missed call tips modal
|
||||||
|
static let hasSeenCallMissedTips: UserDefaultsInfo.BoolKey = "hasSeenCallMissedTips"
|
||||||
|
|
||||||
|
/// Indicates whether the user is registered for APNS (ie. "Fast Mode" notifications)
|
||||||
|
static let isUsingFullAPNs: UserDefaultsInfo.BoolKey = "isUsingFullAPNs"
|
||||||
|
|
||||||
|
/// Indicates whether the device was unlinked from an account
|
||||||
|
///
|
||||||
|
/// **Note:** This doesn't seem to be properly used (we basically just maintain the existing value)
|
||||||
|
static let wasUnlinked: UserDefaultsInfo.BoolKey = "wasUnlinked"
|
||||||
|
|
||||||
|
/// Indicates whether the main app is active, this is set to `true` while the app is in the foreground and `false` when
|
||||||
|
/// the app is in the background
|
||||||
|
static let isMainAppActive: UserDefaultsInfo.BoolKey = "isMainAppActive"
|
||||||
|
|
||||||
|
/// Indicates whether there is an ongoing call
|
||||||
|
static let isCallOngoing: UserDefaultsInfo.BoolKey = "isCallOngoing"
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension UserDefaultsInfo.DateKey {
|
||||||
|
/// The date/time when the users profile picture was last uploaded to the server (used to rate-limit re-uploading)
|
||||||
|
static let lastProfilePictureUpload: UserDefaultsInfo.DateKey = "lastProfilePictureUpload"
|
||||||
|
|
||||||
|
/// The date/time when the device last updated the default open group room images (used to rate-limit re-downloading)
|
||||||
|
static let lastOpenGroupImageUpdate: UserDefaultsInfo.DateKey = "lastOpenGroupImageUpdate"
|
||||||
|
|
||||||
|
/// The date/time when any open group last had a successful poll (used as a fallback date/time if the open group hasn't been polled
|
||||||
|
/// this session)
|
||||||
|
static let lastOpen: UserDefaultsInfo.DateKey = "lastOpen"
|
||||||
|
|
||||||
|
/// The date/time when the last garbage collection was performed (used to rate-limit garbage collection)
|
||||||
|
static let lastGarbageCollection: UserDefaultsInfo.DateKey = "lastGarbageCollection"
|
||||||
|
|
||||||
|
/// The date/time when we last subscribed for push notifications (used to rate-limit calling our subscription endpoint)
|
||||||
|
static let lastPushNotificationSync: UserDefaultsInfo.DateKey = "lastPushNotificationSync"
|
||||||
|
|
||||||
|
/// The date/time when we received a call pre-offer (used to suppress call notifications which are too old)
|
||||||
|
static let lastCallPreOffer: UserDefaultsInfo.DateKey = "lastCallPreOffer"
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension UserDefaultsInfo.DoubleKey {
|
||||||
|
/// The timestamp when we last uploaded the users push token (used to rate-limit calling our subscription endpoint)
|
||||||
|
///
|
||||||
|
/// **Note:** Looks like this replicates the `lastPushNotificationSync` behaviour
|
||||||
|
static let lastDeviceTokenUpload: UserDefaultsInfo.DoubleKey = "lastDeviceTokenUploadTime"
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension UserDefaultsInfo.IntKey {
|
||||||
|
/// The latest hardfork value returned when interacting with a service node
|
||||||
|
static let hardfork: UserDefaultsInfo.IntKey = "hardfork"
|
||||||
|
|
||||||
|
/// The latest softfork value returned when interacting with a service node
|
||||||
|
static let softfork: UserDefaultsInfo.IntKey = "softfork"
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension UserDefaultsInfo.StringKey {
|
||||||
|
/// The most recently subscribed APNS token
|
||||||
|
static let deviceToken: UserDefaultsInfo.StringKey = "deviceToken"
|
||||||
|
|
||||||
|
/// The warning to show at the top of the app
|
||||||
|
static let topBannerWarningToShow: UserDefaultsInfo.StringKey = "topBannerWarningToShow"
|
||||||
|
}
|
|
@ -94,8 +94,6 @@ NSString *NSStringForUIApplicationState(UIApplicationState value);
|
||||||
|
|
||||||
- (NSString *)appSharedDataDirectoryPath;
|
- (NSString *)appSharedDataDirectoryPath;
|
||||||
|
|
||||||
- (NSUserDefaults *)appUserDefaults;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
id<AppContext> CurrentAppContext(void);
|
id<AppContext> CurrentAppContext(void);
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface NSUserDefaults (OWS)
|
|
||||||
|
|
||||||
+ (NSUserDefaults *)appUserDefaults;
|
|
||||||
|
|
||||||
+ (void)migrateToSharedUserDefaults;
|
|
||||||
|
|
||||||
+ (void)removeAll;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
|
@ -1,45 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "NSUserDefaults+OWS.h"
|
|
||||||
#import "AppContext.h"
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@implementation NSUserDefaults (OWS)
|
|
||||||
|
|
||||||
+ (NSUserDefaults *)appUserDefaults
|
|
||||||
{
|
|
||||||
return CurrentAppContext().appUserDefaults;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)migrateToSharedUserDefaults
|
|
||||||
{
|
|
||||||
NSUserDefaults *appUserDefaults = self.appUserDefaults;
|
|
||||||
|
|
||||||
NSDictionary<NSString *, id> *dictionary = [NSUserDefaults standardUserDefaults].dictionaryRepresentation;
|
|
||||||
for (NSString *key in dictionary) {
|
|
||||||
id value = dictionary[key];
|
|
||||||
[appUserDefaults setObject:value forKey:key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)removeAll
|
|
||||||
{
|
|
||||||
[NSUserDefaults.standardUserDefaults removeAll];
|
|
||||||
[self.appUserDefaults removeAll];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeAll
|
|
||||||
{
|
|
||||||
NSDictionary<NSString *, id> *dictionary = self.dictionaryRepresentation;
|
|
||||||
for (NSString *key in dictionary) {
|
|
||||||
[self removeObjectForKey:key];
|
|
||||||
}
|
|
||||||
[self synchronize];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
|
@ -1,106 +0,0 @@
|
||||||
import Foundation
|
|
||||||
|
|
||||||
// MARK: - Singleton
|
|
||||||
public extension Singleton {
|
|
||||||
static let standardUserDefaults: SingletonInfo.Config<UserDefaultsType> = SingletonInfo.create { _ in
|
|
||||||
UserDefaults.standard
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - UserDefaultsType
|
|
||||||
|
|
||||||
public protocol UserDefaultsType: AnyObject {
|
|
||||||
func object(forKey defaultName: String) -> Any?
|
|
||||||
func string(forKey defaultName: String) -> String?
|
|
||||||
func array(forKey defaultName: String) -> [Any]?
|
|
||||||
func dictionary(forKey defaultName: String) -> [String : Any]?
|
|
||||||
func data(forKey defaultName: String) -> Data?
|
|
||||||
func stringArray(forKey defaultName: String) -> [String]?
|
|
||||||
func integer(forKey defaultName: String) -> Int
|
|
||||||
func float(forKey defaultName: String) -> Float
|
|
||||||
func double(forKey defaultName: String) -> Double
|
|
||||||
func bool(forKey defaultName: String) -> Bool
|
|
||||||
func url(forKey defaultName: String) -> URL?
|
|
||||||
|
|
||||||
func set(_ value: Any?, forKey defaultName: String)
|
|
||||||
func set(_ value: Int, forKey defaultName: String)
|
|
||||||
func set(_ value: Float, forKey defaultName: String)
|
|
||||||
func set(_ value: Double, forKey defaultName: String)
|
|
||||||
func set(_ value: Bool, forKey defaultName: String)
|
|
||||||
func set(_ url: URL?, forKey defaultName: String)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UserDefaults: UserDefaultsType {}
|
|
||||||
|
|
||||||
public enum SNUserDefaults {
|
|
||||||
|
|
||||||
public enum Bool: Swift.String {
|
|
||||||
case hasSyncedInitialConfiguration = "hasSyncedConfiguration"
|
|
||||||
case hasSeenLinkPreviewSuggestion
|
|
||||||
case hasSeenCallIPExposureWarning
|
|
||||||
case hasSeenCallMissedTips
|
|
||||||
case isUsingFullAPNs
|
|
||||||
case wasUnlinked
|
|
||||||
case isMainAppActive
|
|
||||||
case isCallOngoing
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Date: Swift.String {
|
|
||||||
case lastProfilePictureUpload
|
|
||||||
case lastOpenGroupImageUpdate
|
|
||||||
case lastOpen
|
|
||||||
case lastGarbageCollection
|
|
||||||
case lastPushNotificationSync
|
|
||||||
case lastCallPreOffer
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Double: Swift.String {
|
|
||||||
case lastDeviceTokenUpload = "lastDeviceTokenUploadTime"
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Int: Swift.String {
|
|
||||||
case appMode
|
|
||||||
case hardfork
|
|
||||||
case softfork
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum String : Swift.String {
|
|
||||||
case deviceToken
|
|
||||||
case topBannerWarningToShow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension UserDefaults {
|
|
||||||
static let applicationGroup: String = "group.com.loki-project.loki-messenger"
|
|
||||||
|
|
||||||
@objc static var sharedLokiProject: UserDefaults? {
|
|
||||||
UserDefaults(suiteName: UserDefaults.applicationGroup)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension UserDefaultsType {
|
|
||||||
subscript(bool: SNUserDefaults.Bool) -> Bool {
|
|
||||||
get { return self.bool(forKey: bool.rawValue) }
|
|
||||||
set { set(newValue, forKey: bool.rawValue) }
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript(date: SNUserDefaults.Date) -> Date? {
|
|
||||||
get { return self.object(forKey: date.rawValue) as? Date }
|
|
||||||
set { set(newValue, forKey: date.rawValue) }
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript(double: SNUserDefaults.Double) -> Double {
|
|
||||||
get { return self.double(forKey: double.rawValue) }
|
|
||||||
set { set(newValue, forKey: double.rawValue) }
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript(int: SNUserDefaults.Int) -> Int {
|
|
||||||
get { return self.integer(forKey: int.rawValue) }
|
|
||||||
set { set(newValue, forKey: int.rawValue) }
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript(string: SNUserDefaults.String) -> String? {
|
|
||||||
get { return self.string(forKey: string.rawValue) }
|
|
||||||
set { set(newValue, forKey: string.rawValue) }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,6 @@ FOUNDATION_EXPORT const unsigned char SessionUtilitiesKitVersionString[];
|
||||||
#import <SessionUtilitiesKit/MIMETypeUtil.h>
|
#import <SessionUtilitiesKit/MIMETypeUtil.h>
|
||||||
#import <SessionUtilitiesKit/NSData+Image.h>
|
#import <SessionUtilitiesKit/NSData+Image.h>
|
||||||
#import <SessionUtilitiesKit/NSTimer+Proxying.h>
|
#import <SessionUtilitiesKit/NSTimer+Proxying.h>
|
||||||
#import <SessionUtilitiesKit/NSUserDefaults+OWS.h>
|
|
||||||
#import <SessionUtilitiesKit/OWSFileSystem.h>
|
#import <SessionUtilitiesKit/OWSFileSystem.h>
|
||||||
#import <SessionUtilitiesKit/OWSMath.h>
|
#import <SessionUtilitiesKit/OWSMath.h>
|
||||||
#import <SessionUtilitiesKit/UIImage+OWS.h>
|
#import <SessionUtilitiesKit/UIImage+OWS.h>
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class AppVersion {
|
||||||
|
private static var _shared: AppVersion?
|
||||||
|
public static var shared: AppVersion {
|
||||||
|
let result: AppVersion = (_shared ?? AppVersion())
|
||||||
|
_shared = result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public var isFirstLaunch: Bool { self.firstAppVersion != nil }
|
||||||
|
|
||||||
|
public let isValid: Bool
|
||||||
|
public let currentAppVersion: String
|
||||||
|
|
||||||
|
/// The version of the app when it was first launched (`nil` if the app has never been launched before)
|
||||||
|
public var firstAppVersion: String?
|
||||||
|
|
||||||
|
/// The version of the app the last time it was launched (`nil` if the app has never been launched before)
|
||||||
|
public var lastAppVersion: String?
|
||||||
|
|
||||||
|
public var lastCompletedLaunchAppVersion: String?
|
||||||
|
public var lastCompletedLaunchMainAppVersion: String?
|
||||||
|
public var lastCompletedLaunchSAEAppVersion: String?
|
||||||
|
|
||||||
|
// MARK: - Initialization
|
||||||
|
|
||||||
|
private init(using dependencies: Dependencies = Dependencies()) {
|
||||||
|
guard let currentAppVersion: String = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
|
||||||
|
self.isValid = false
|
||||||
|
self.currentAppVersion = ""
|
||||||
|
self.firstAppVersion = nil
|
||||||
|
self.lastAppVersion = nil
|
||||||
|
self.lastCompletedLaunchAppVersion = nil
|
||||||
|
self.lastCompletedLaunchMainAppVersion = nil
|
||||||
|
self.lastCompletedLaunchSAEAppVersion = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let oldFirstAppVersion: String? = dependencies[defaults: .appGroup, key: .firstAppVersion]
|
||||||
|
|
||||||
|
self.isValid = true
|
||||||
|
self.currentAppVersion = currentAppVersion
|
||||||
|
self.firstAppVersion = dependencies[defaults: .appGroup, key: .firstAppVersion]
|
||||||
|
.defaulting(to: currentAppVersion)
|
||||||
|
self.lastAppVersion = dependencies[defaults: .appGroup, key: .lastAppVersion]
|
||||||
|
self.lastCompletedLaunchAppVersion = dependencies[defaults: .appGroup, key: .lastCompletedLaunchAppVersion]
|
||||||
|
self.lastCompletedLaunchMainAppVersion = dependencies[defaults: .appGroup, key: .lastCompletedLaunchMainAppVersion]
|
||||||
|
self.lastCompletedLaunchSAEAppVersion = dependencies[defaults: .appGroup, key: .lastCompletedLaunchSAEAppVersion]
|
||||||
|
|
||||||
|
// Ensure the value for the "first launched version".
|
||||||
|
if oldFirstAppVersion == nil {
|
||||||
|
dependencies[defaults: .appGroup, key: .firstAppVersion] = currentAppVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the value for the "most recently launched version".
|
||||||
|
dependencies[defaults: .appGroup, key: .lastAppVersion] = currentAppVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Functions
|
||||||
|
|
||||||
|
public static func configure(using dependencies: Dependencies) {
|
||||||
|
_shared = AppVersion(using: dependencies)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func appLaunchDidComplete(using dependencies: Dependencies) {
|
||||||
|
lastCompletedLaunchAppVersion = currentAppVersion
|
||||||
|
|
||||||
|
// Update the value for the "most recently launch-completed version".
|
||||||
|
dependencies[defaults: .appGroup, key: .lastCompletedLaunchAppVersion] = currentAppVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mainAppLaunchDidComplete(using dependencies: Dependencies) {
|
||||||
|
lastCompletedLaunchMainAppVersion = currentAppVersion
|
||||||
|
|
||||||
|
dependencies[defaults: .appGroup, key: .lastCompletedLaunchMainAppVersion] = currentAppVersion
|
||||||
|
appLaunchDidComplete(using: dependencies)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func saeLaunchDidComplete(using dependencies: Dependencies) {
|
||||||
|
lastCompletedLaunchSAEAppVersion = currentAppVersion
|
||||||
|
dependencies[defaults: .appGroup, key: .lastCompletedLaunchSAEAppVersion] = currentAppVersion
|
||||||
|
appLaunchDidComplete(using: dependencies)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - UserDefaults Keys
|
||||||
|
|
||||||
|
private extension UserDefaultsInfo.StringKey {
|
||||||
|
/// The version of the app when it was first launched
|
||||||
|
static let firstAppVersion: UserDefaultsInfo.StringKey = "kNSUserDefaults_FirstAppVersion"
|
||||||
|
|
||||||
|
/// The version of the app when it was last launched
|
||||||
|
static let lastAppVersion: UserDefaultsInfo.StringKey = "kNSUserDefaults_LastVersion"
|
||||||
|
|
||||||
|
static let lastCompletedLaunchAppVersion: UserDefaultsInfo.StringKey = "kNSUserDefaults_LastCompletedLaunchAppVersion"
|
||||||
|
static let lastCompletedLaunchMainAppVersion: UserDefaultsInfo.StringKey = "kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp"
|
||||||
|
static let lastCompletedLaunchSAEAppVersion: UserDefaultsInfo.StringKey = "kNSUserDefaults_LastCompletedLaunchAppVersion_SAE"
|
||||||
|
}
|
|
@ -3,5 +3,4 @@
|
||||||
FOUNDATION_EXPORT double SignalUtilitiesKitVersionNumber;
|
FOUNDATION_EXPORT double SignalUtilitiesKitVersionNumber;
|
||||||
FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[];
|
FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[];
|
||||||
|
|
||||||
#import <SignalUtilitiesKit/AppVersion.h>
|
|
||||||
#import <SignalUtilitiesKit/OWSViewController.h>
|
#import <SignalUtilitiesKit/OWSViewController.h>
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface AppVersion : NSObject
|
|
||||||
|
|
||||||
// The properties are updated immediately after launch.
|
|
||||||
@property (atomic, readonly) NSString *firstAppVersion;
|
|
||||||
@property (atomic, nullable, readonly) NSString *lastAppVersion;
|
|
||||||
@property (atomic, readonly) NSString *currentAppVersion;
|
|
||||||
|
|
||||||
// There properties aren't updated until appLaunchDidComplete is called.
|
|
||||||
@property (atomic, nullable, readonly) NSString *lastCompletedLaunchAppVersion;
|
|
||||||
@property (atomic, nullable, readonly) NSString *lastCompletedLaunchMainAppVersion;
|
|
||||||
@property (atomic, nullable, readonly) NSString *lastCompletedLaunchSAEAppVersion;
|
|
||||||
|
|
||||||
- (instancetype)init NS_UNAVAILABLE;
|
|
||||||
|
|
||||||
+ (instancetype)sharedInstance;
|
|
||||||
|
|
||||||
- (void)mainAppLaunchDidComplete;
|
|
||||||
- (void)saeLaunchDidComplete;
|
|
||||||
|
|
||||||
- (BOOL)isFirstLaunch;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
|
@ -1,133 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "AppVersion.h"
|
|
||||||
#import "NSUserDefaults+OWS.h"
|
|
||||||
#import <SignalCoreKit/OWSAsserts.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
NSString *const kNSUserDefaults_FirstAppVersion = @"kNSUserDefaults_FirstAppVersion";
|
|
||||||
NSString *const kNSUserDefaults_LastAppVersion = @"kNSUserDefaults_LastVersion";
|
|
||||||
NSString *const kNSUserDefaults_LastCompletedLaunchAppVersion = @"kNSUserDefaults_LastCompletedLaunchAppVersion";
|
|
||||||
NSString *const kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp
|
|
||||||
= @"kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp";
|
|
||||||
NSString *const kNSUserDefaults_LastCompletedLaunchAppVersion_SAE
|
|
||||||
= @"kNSUserDefaults_LastCompletedLaunchAppVersion_SAE";
|
|
||||||
|
|
||||||
@interface AppVersion ()
|
|
||||||
|
|
||||||
@property (atomic) NSString *firstAppVersion;
|
|
||||||
@property (atomic, nullable) NSString *lastAppVersion;
|
|
||||||
@property (atomic) NSString *currentAppVersion;
|
|
||||||
|
|
||||||
@property (atomic, nullable) NSString *lastCompletedLaunchAppVersion;
|
|
||||||
@property (atomic, nullable) NSString *lastCompletedLaunchMainAppVersion;
|
|
||||||
@property (atomic, nullable) NSString *lastCompletedLaunchSAEAppVersion;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
@implementation AppVersion
|
|
||||||
|
|
||||||
+ (instancetype)sharedInstance
|
|
||||||
{
|
|
||||||
static AppVersion *instance = nil;
|
|
||||||
static dispatch_once_t onceToken;
|
|
||||||
dispatch_once(&onceToken, ^{
|
|
||||||
instance = [AppVersion new];
|
|
||||||
[instance configure];
|
|
||||||
});
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)configure {
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
self.currentAppVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
|
||||||
|
|
||||||
// The version of the app when it was first launched.
|
|
||||||
// nil if the app has never been launched before.
|
|
||||||
self.firstAppVersion = [[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_FirstAppVersion];
|
|
||||||
// The version of the app the last time it was launched.
|
|
||||||
// nil if the app has never been launched before.
|
|
||||||
self.lastAppVersion = [[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastAppVersion];
|
|
||||||
self.lastCompletedLaunchAppVersion =
|
|
||||||
[[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastCompletedLaunchAppVersion];
|
|
||||||
self.lastCompletedLaunchMainAppVersion =
|
|
||||||
[[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp];
|
|
||||||
self.lastCompletedLaunchSAEAppVersion =
|
|
||||||
[[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastCompletedLaunchAppVersion_SAE];
|
|
||||||
|
|
||||||
// Ensure the value for the "first launched version".
|
|
||||||
if (!self.firstAppVersion) {
|
|
||||||
self.firstAppVersion = self.currentAppVersion;
|
|
||||||
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion forKey:kNSUserDefaults_FirstAppVersion];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the value for the "most recently launched version".
|
|
||||||
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion forKey:kNSUserDefaults_LastAppVersion];
|
|
||||||
[[NSUserDefaults appUserDefaults] synchronize];
|
|
||||||
|
|
||||||
// The long version string looks like an IPv4 address.
|
|
||||||
// To prevent the log scrubber from scrubbing it,
|
|
||||||
// we replace . with _.
|
|
||||||
NSString *longVersionString = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]
|
|
||||||
stringByReplacingOccurrencesOfString:@"."
|
|
||||||
withString:@"_"];
|
|
||||||
|
|
||||||
OWSLogInfo(@"firstAppVersion: %@", self.firstAppVersion);
|
|
||||||
OWSLogInfo(@"lastAppVersion: %@", self.lastAppVersion);
|
|
||||||
OWSLogInfo(@"currentAppVersion: %@ (%@)", self.currentAppVersion, longVersionString);
|
|
||||||
|
|
||||||
OWSLogInfo(@"lastCompletedLaunchAppVersion: %@", self.lastCompletedLaunchAppVersion);
|
|
||||||
OWSLogInfo(@"lastCompletedLaunchMainAppVersion: %@", self.lastCompletedLaunchMainAppVersion);
|
|
||||||
OWSLogInfo(@"lastCompletedLaunchSAEAppVersion: %@", self.lastCompletedLaunchSAEAppVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)appLaunchDidComplete
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
OWSLogInfo(@"appLaunchDidComplete");
|
|
||||||
|
|
||||||
self.lastCompletedLaunchAppVersion = self.currentAppVersion;
|
|
||||||
|
|
||||||
// Update the value for the "most recently launch-completed version".
|
|
||||||
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion
|
|
||||||
forKey:kNSUserDefaults_LastCompletedLaunchAppVersion];
|
|
||||||
[[NSUserDefaults appUserDefaults] synchronize];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mainAppLaunchDidComplete
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
self.lastCompletedLaunchMainAppVersion = self.currentAppVersion;
|
|
||||||
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion
|
|
||||||
forKey:kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp];
|
|
||||||
|
|
||||||
[self appLaunchDidComplete];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)saeLaunchDidComplete
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
self.lastCompletedLaunchSAEAppVersion = self.currentAppVersion;
|
|
||||||
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion
|
|
||||||
forKey:kNSUserDefaults_LastCompletedLaunchAppVersion_SAE];
|
|
||||||
|
|
||||||
[self appLaunchDidComplete];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isFirstLaunch
|
|
||||||
{
|
|
||||||
return self.firstAppVersion != nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
|
@ -7,6 +7,7 @@ import Foundation
|
||||||
public class TestDependencies: Dependencies {
|
public class TestDependencies: Dependencies {
|
||||||
private var singletonInstances: [Int: Any] = [:]
|
private var singletonInstances: [Int: Any] = [:]
|
||||||
private var cacheInstances: [Int: MutableCacheType] = [:]
|
private var cacheInstances: [Int: MutableCacheType] = [:]
|
||||||
|
private var defaultsInstances: [Int: (any UserDefaultsType)] = [:]
|
||||||
|
|
||||||
// MARK: - Subscript Access
|
// MARK: - Subscript Access
|
||||||
|
|
||||||
|
@ -28,6 +29,15 @@ public class TestDependencies: Dependencies {
|
||||||
set { cacheInstances[cache.key] = newValue.map { cache.mutableInstance($0) } }
|
set { cacheInstances[cache.key] = newValue.map { cache.mutableInstance($0) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>) -> U {
|
||||||
|
return getValueSettingIfNull(defaults: defaults, &defaultsInstances)
|
||||||
|
}
|
||||||
|
|
||||||
|
public subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>) -> U? {
|
||||||
|
get { return (defaultsInstances[defaults.key] as? U) }
|
||||||
|
set { defaultsInstances[defaults.key] = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Timing and Async Handling
|
// MARK: - Timing and Async Handling
|
||||||
|
|
||||||
private var _dateNow: Atomic<Date?>
|
private var _dateNow: Atomic<Date?>
|
||||||
|
@ -120,4 +130,17 @@ public class TestDependencies: Dependencies {
|
||||||
|
|
||||||
return cache.immutableInstance(value)
|
return cache.immutableInstance(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@discardableResult private func getValueSettingIfNull<U>(
|
||||||
|
defaults: UserDefaultsInfo.Config<U>,
|
||||||
|
_ store: inout [Int: (any UserDefaultsType)]
|
||||||
|
) -> U {
|
||||||
|
guard let value: U = (store[defaults.key] as? U) else {
|
||||||
|
let value: U = defaults.createInstance(self)
|
||||||
|
store[defaults.key] = value
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue