Finished removing usages to non-theme colour variables
Updated theming throughout Fixed a couple of bugs with the media gallery
This commit is contained in:
parent
1bc6b0bdba
commit
f7fd15dae0
|
@ -9,11 +9,8 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
1FFD68A448D5A1439F2F02FD /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DBA125424EDD2417B515C63A /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionShareExtension.framework */; };
|
||||
3289CA2E9E89DA9D4D52A90C /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SignalUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BF4561630A52BE96F164CF6 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SignalUtilitiesKit.framework */; };
|
||||
340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC896204DAC8C007AEB0F /* OWSQRCodeScanningViewController.m */; };
|
||||
3427C64320F500E000EEC730 /* OWSMessageTimerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3427C64220F500DF00EEC730 /* OWSMessageTimerView.m */; };
|
||||
3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3430FE171F7751D4000EC51B /* GiphyAPI.swift */; };
|
||||
34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34330AA21E79686200DF2FB9 /* OWSProgressView.m */; };
|
||||
34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34386A53207D271C009F5D9C /* NeverClearView.swift */; };
|
||||
34661FB820C1C0D60056EDD6 /* message_sent.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 34661FB720C1C0D60056EDD6 /* message_sent.aiff */; };
|
||||
346B66311F4E29B200E5122F /* CropScaleImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */; };
|
||||
3478504C1FD7496D007B8332 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B66DBF4919D5BBC8006EA940 /* Images.xcassets */; };
|
||||
|
@ -37,7 +34,6 @@
|
|||
34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0511F7E8EA30066283D /* GiphyDownloader.swift */; };
|
||||
34D5CCA91EAE3D30005515DB /* AvatarViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CCA81EAE3D30005515DB /* AvatarViewHelper.m */; };
|
||||
34D99CE4217509C2000AFB39 /* AppEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D99CE3217509C1000AFB39 /* AppEnvironment.swift */; };
|
||||
34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */; };
|
||||
34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */; };
|
||||
4503F1BE20470A5B00CEE724 /* classic-quiet.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 4503F1BB20470A5B00CEE724 /* classic-quiet.aifc */; };
|
||||
4503F1BF20470A5B00CEE724 /* classic.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 4503F1BC20470A5B00CEE724 /* classic.aifc */; };
|
||||
|
@ -54,7 +50,6 @@
|
|||
455A16DE1F1FEA0000F86704 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 455A16DC1F1FEA0000F86704 /* MetalKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
45847E871E4283C30080EAB3 /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45847E861E4283C30080EAB3 /* Intents.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
45A2F005204473A3002E978A /* NewMessage.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 45A2F004204473A3002E978A /* NewMessage.aifc */; };
|
||||
45A6DAD61EBBF85500893231 /* ReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45A6DAD51EBBF85500893231 /* ReminderView.swift */; };
|
||||
45B5360E206DD8BB00D61655 /* UIResponder+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45B5360D206DD8BB00D61655 /* UIResponder+OWS.swift */; };
|
||||
45B74A742044AAB600CD42F8 /* aurora-quiet.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 45B74A5B2044AAB300CD42F8 /* aurora-quiet.aifc */; };
|
||||
45B74A752044AAB600CD42F8 /* synth-quiet.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 45B74A5C2044AAB300CD42F8 /* synth-quiet.aifc */; };
|
||||
|
@ -85,13 +80,11 @@
|
|||
45C0DC1E1E69011F00E04C47 /* UIStoryboard+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45C0DC1D1E69011F00E04C47 /* UIStoryboard+OWS.swift */; };
|
||||
45CB2FA81CB7146C00E1B343 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */; };
|
||||
45CD81EF1DC030E7004C9430 /* SyncPushTokensJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CD81EE1DC030E7004C9430 /* SyncPushTokensJob.swift */; };
|
||||
45E5A6991F61E6DE001E4A8A /* MarqueeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */; };
|
||||
45F32C232057297A00A300D5 /* MediaPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F32C1D205718B000A300D5 /* MediaPageViewController.swift */; };
|
||||
4C090A1B210FD9C7001FD7F9 /* HapticFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C090A1A210FD9C7001FD7F9 /* HapticFeedback.swift */; };
|
||||
4C1885D2218F8E1C00B67051 /* PhotoGridViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1885D1218F8E1C00B67051 /* PhotoGridViewCell.swift */; };
|
||||
4C21D5D8223AC60F00EF8A77 /* PhotoCapture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C21D5D7223AC60F00EF8A77 /* PhotoCapture.swift */; };
|
||||
4C4AE6A1224AF35700D4AF6F /* SendMediaNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4AE69F224AF21900D4AF6F /* SendMediaNavigationController.swift */; };
|
||||
4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */; };
|
||||
4C586926224FAB83003FD070 /* AVAudioSession+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C586925224FAB83003FD070 /* AVAudioSession+OWS.m */; };
|
||||
4C63CC00210A620B003AE45C /* SignalTSan.supp in Resources */ = {isa = PBXBuildFile; fileRef = 4C63CBFF210A620B003AE45C /* SignalTSan.supp */; };
|
||||
4C6F527C20FFE8400097DEEE /* SignalUBSan.supp in Resources */ = {isa = PBXBuildFile; fileRef = 4C6F527B20FFE8400097DEEE /* SignalUBSan.supp */; };
|
||||
|
@ -154,8 +147,8 @@
|
|||
7BAF54D427ACCF01003D12F8 /* SAEScreenLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.swift */; };
|
||||
7BAF54D827ACD0E3003D12F8 /* ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D527ACD0E2003D12F8 /* ReusableView.swift */; };
|
||||
7BAF54DC27ACD12B003D12F8 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54DB27ACD12B003D12F8 /* UIColor+Extensions.swift */; };
|
||||
7BBBDC462875600700747E59 /* DocumentTitleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */; };
|
||||
7BBBDC44286EAD2D00747E59 /* TappableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC43286EAD2D00747E59 /* TappableLabel.swift */; };
|
||||
7BBBDC462875600700747E59 /* DocumentTitleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */; };
|
||||
7BC01A3E241F40AB00BC7C55 /* NotificationServiceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC01A3D241F40AB00BC7C55 /* NotificationServiceExtension.swift */; };
|
||||
7BC01A42241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7BC01A3B241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
7BC707F227290ACB002817AD /* SessionCallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC707F127290ACB002817AD /* SessionCallManager.swift */; };
|
||||
|
@ -279,7 +272,6 @@
|
|||
C300A5F22554B09800555489 /* MessageSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = C300A5F12554B09800555489 /* MessageSender.swift */; };
|
||||
C300A60D2554B31900555489 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5CE2553860700C340D1 /* Logging.swift */; };
|
||||
C302093E25DCBF08001F572D /* MentionSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C302093D25DCBF07001F572D /* MentionSelectionView.swift */; };
|
||||
C31A6C5A247F214E001123EF /* UIView+Glow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31A6C59247F214E001123EF /* UIView+Glow.swift */; };
|
||||
C31A6C5C247F2CF3001123EF /* CGRect+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31A6C5B247F2CF3001123EF /* CGRect+Utilities.swift */; };
|
||||
C31D1DE32521718E005D4DA8 /* UserSelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */; };
|
||||
C32824D325C9F9790062D0A7 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; };
|
||||
|
@ -309,7 +301,6 @@
|
|||
C32C6018256E07F9003C73A2 /* NSUserDefaults+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB51255A580D00E217F9 /* NSUserDefaults+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C33100082558FF6D00070591 /* NewConversationButtonSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83F2B85240C7B8F000A54AB /* NewConversationButtonSet.swift */; };
|
||||
C33100092558FF6D00070591 /* UserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DDC25217014005D4DA8 /* UserCell.swift */; };
|
||||
C33100142558FFC200070591 /* UIImage+Tinting.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33100132558FFC200070591 /* UIImage+Tinting.swift */; };
|
||||
C33100282559000A00070591 /* UIView+Rendering.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33100272559000A00070591 /* UIView+Rendering.swift */; };
|
||||
C3310033255900A400070591 /* Notification+AppMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3310032255900A400070591 /* Notification+AppMode.swift */; };
|
||||
C331FF1F2558F9D300070591 /* SessionUIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C331FF1D2558F9D300070591 /* SessionUIKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
|
@ -389,11 +380,9 @@
|
|||
C38EF00C255B61CC007E1867 /* SignalUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; };
|
||||
C38EF22B255B6D5D007E1867 /* ShareViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF226255B6D5D007E1867 /* ShareViewDelegate.swift */; };
|
||||
C38EF22C255B6D5D007E1867 /* OWSVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF227255B6D5D007E1867 /* OWSVideoPlayer.swift */; };
|
||||
C38EF243255B6D67007E1867 /* UIViewController+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF236255B6D65007E1867 /* UIViewController+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38EF245255B6D67007E1867 /* UIFont+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF238255B6D66007E1867 /* UIFont+OWS.m */; };
|
||||
C38EF246255B6D67007E1867 /* UIFont+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF239255B6D66007E1867 /* UIFont+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38EF247255B6D67007E1867 /* NSAttributedString+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF23A255B6D66007E1867 /* NSAttributedString+OWS.m */; };
|
||||
C38EF248255B6D67007E1867 /* UIViewController+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF23B255B6D66007E1867 /* UIViewController+OWS.m */; };
|
||||
C38EF249255B6D67007E1867 /* UIColor+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF23C255B6D66007E1867 /* UIColor+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38EF24C255B6D67007E1867 /* NSAttributedString+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF23F255B6D67007E1867 /* NSAttributedString+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38EF24D255B6D67007E1867 /* UIView+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF240255B6D67007E1867 /* UIView+OWS.swift */; };
|
||||
|
@ -406,15 +395,11 @@
|
|||
C38EF2B4255B6D9C007E1867 /* UIView+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2B2255B6D9C007E1867 /* UIView+Utilities.swift */; };
|
||||
C38EF30C255B6DBF007E1867 /* ScreenLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2E2255B6DB9007E1867 /* ScreenLock.swift */; };
|
||||
C38EF31C255B6DBF007E1867 /* Searcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F2255B6DBC007E1867 /* Searcher.swift */; };
|
||||
C38EF31D255B6DBF007E1867 /* UIImage+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */; };
|
||||
C38EF324255B6DBF007E1867 /* Bench.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2FA255B6DBD007E1867 /* Bench.swift */; };
|
||||
C38EF32A255B6DBF007E1867 /* UIUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF300255B6DBD007E1867 /* UIUtil.m */; };
|
||||
C38EF32B255B6DBF007E1867 /* OWSFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF301255B6DBD007E1867 /* OWSFormat.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38EF32E255B6DBF007E1867 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF304255B6DBE007E1867 /* ImageCache.swift */; };
|
||||
C38EF32F255B6DBF007E1867 /* OWSFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF305255B6DBE007E1867 /* OWSFormat.m */; };
|
||||
C38EF331255B6DBF007E1867 /* UIGestureRecognizer+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */; };
|
||||
C38EF334255B6DBF007E1867 /* UIUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF30A255B6DBE007E1867 /* UIUtil.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38EF359255B6DCC007E1867 /* SheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF33F255B6DC5007E1867 /* SheetViewController.swift */; };
|
||||
C38EF35D255B6DCC007E1867 /* OWSNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF343255B6DC5007E1867 /* OWSNavigationController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38EF35E255B6DCC007E1867 /* OWSViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF344255B6DC5007E1867 /* OWSViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38EF363255B6DCC007E1867 /* ModalActivityIndicatorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF349255B6DC7007E1867 /* ModalActivityIndicatorViewController.swift */; };
|
||||
|
@ -429,7 +414,6 @@
|
|||
C38EF38A255B6DD2007E1867 /* AttachmentCaptionToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF381255B6DD1007E1867 /* AttachmentCaptionToolbar.swift */; };
|
||||
C38EF38B255B6DD2007E1867 /* AttachmentPrepViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF382255B6DD1007E1867 /* AttachmentPrepViewController.swift */; };
|
||||
C38EF38C255B6DD2007E1867 /* ApprovalRailCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF383255B6DD1007E1867 /* ApprovalRailCellView.swift */; };
|
||||
C38EF38D255B6DD2007E1867 /* AttachmentCaptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF384255B6DD2007E1867 /* AttachmentCaptionViewController.swift */; };
|
||||
C38EF3B8255B6DE7007E1867 /* ImageEditorTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3A8255B6DE4007E1867 /* ImageEditorTextViewController.swift */; };
|
||||
C38EF3B9255B6DE7007E1867 /* ImageEditorPinchGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3A9255B6DE4007E1867 /* ImageEditorPinchGestureRecognizer.swift */; };
|
||||
C38EF3BA255B6DE7007E1867 /* ImageEditorItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3AA255B6DE4007E1867 /* ImageEditorItem.swift */; };
|
||||
|
@ -460,7 +444,6 @@
|
|||
C38EF402255B6DF7007E1867 /* CommonStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3E4255B6DF4007E1867 /* CommonStrings.swift */; };
|
||||
C38EF405255B6DF7007E1867 /* OWSButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3E7255B6DF5007E1867 /* OWSButton.swift */; };
|
||||
C38EF407255B6DF7007E1867 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3E9255B6DF6007E1867 /* Toast.swift */; };
|
||||
C38EF40A255B6DF7007E1867 /* OWSFlatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3EC255B6DF6007E1867 /* OWSFlatButton.swift */; };
|
||||
C38EF40B255B6DF7007E1867 /* TappableStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3ED255B6DF6007E1867 /* TappableStackView.swift */; };
|
||||
C38EF40C255B6DF7007E1867 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3EE255B6DF6007E1867 /* GradientView.swift */; };
|
||||
C38EF48A255B7E3F007E1867 /* SessionUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C331FF1B2558F9D300070591 /* SessionUIKit.framework */; };
|
||||
|
@ -533,7 +516,6 @@
|
|||
C3D9E50E25677A510040E4F3 /* DataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB54255A580D00E217F9 /* DataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; };
|
||||
C3DB66C3260ACCE6001EFC55 /* OpenGroupPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66C2260ACCE6001EFC55 /* OpenGroupPoller.swift */; };
|
||||
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; };
|
||||
C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */; };
|
||||
C3F0A530255C80BC007BE2A3 /* NoopNotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */; };
|
||||
CEE449BA3596483519120D91 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A8A44E3F8AC9282AC5E6E5A /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionMessagingKit.framework */; };
|
||||
|
@ -721,6 +703,9 @@
|
|||
FD71161728D00DA400B47552 /* ThreadSettingsViewModelSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD71161628D00DA400B47552 /* ThreadSettingsViewModelSpec.swift */; };
|
||||
FD71161A28D00E1100B47552 /* NotificationContentViewModelSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD71161928D00E1100B47552 /* NotificationContentViewModelSpec.swift */; };
|
||||
FD71161C28D194FB00B47552 /* MentionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD71161B28D194FB00B47552 /* MentionInfo.swift */; };
|
||||
FD71161E28D9772700B47552 /* UIViewController+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD71161D28D9772700B47552 /* UIViewController+OWS.swift */; };
|
||||
FD71162028D97ABC00B47552 /* UIImage+Tinting.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD71161F28D97ABC00B47552 /* UIImage+Tinting.swift */; };
|
||||
FD71162228D983ED00B47552 /* QRCodeScanningViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD71162128D983ED00B47552 /* QRCodeScanningViewController.swift */; };
|
||||
FD7162DB281B6C440060647B /* TypedTableAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7162DA281B6C440060647B /* TypedTableAlias.swift */; };
|
||||
FD716E6428502DDD00C96BF4 /* CallManagerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD716E6328502DDD00C96BF4 /* CallManagerProtocol.swift */; };
|
||||
FD716E6628502EE200C96BF4 /* CurrentCallProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD716E6528502EE200C96BF4 /* CurrentCallProtocol.swift */; };
|
||||
|
@ -1065,14 +1050,9 @@
|
|||
2581AFACDDDC1404866D7B8C /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.app store release.xcconfig"; path = "Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.app store release.xcconfig"; sourceTree = "<group>"; };
|
||||
2691123A7F231EDD8226C4B5 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionMessagingKit_SessionMessagingKitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionMessagingKit_SessionMessagingKitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
29CF8C79F41BF00B1C2E59A0 /* Pods-SessionUIKit.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SessionUIKit.app store release.xcconfig"; path = "Target Support Files/Pods-SessionUIKit/Pods-SessionUIKit.app store release.xcconfig"; sourceTree = "<group>"; };
|
||||
340FC888204DAC8C007AEB0F /* OWSQRCodeScanningViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSQRCodeScanningViewController.h; sourceTree = "<group>"; };
|
||||
340FC896204DAC8C007AEB0F /* OWSQRCodeScanningViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSQRCodeScanningViewController.m; sourceTree = "<group>"; };
|
||||
3427C64120F500DE00EEC730 /* OWSMessageTimerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageTimerView.h; sourceTree = "<group>"; };
|
||||
3427C64220F500DF00EEC730 /* OWSMessageTimerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageTimerView.m; sourceTree = "<group>"; };
|
||||
3430FE171F7751D4000EC51B /* GiphyAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GiphyAPI.swift; sourceTree = "<group>"; };
|
||||
34330AA11E79686200DF2FB9 /* OWSProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProgressView.h; sourceTree = "<group>"; };
|
||||
34330AA21E79686200DF2FB9 /* OWSProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProgressView.m; sourceTree = "<group>"; };
|
||||
34386A53207D271C009F5D9C /* NeverClearView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeverClearView.swift; sourceTree = "<group>"; };
|
||||
34480B371FD092A900BC14EF /* SignalShareExtension-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SignalShareExtension-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
34480B381FD092E300BC14EF /* SessionShareExtension-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SessionShareExtension-Prefix.pch"; sourceTree = "<group>"; };
|
||||
34661FB720C1C0D60056EDD6 /* message_sent.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; name = message_sent.aiff; path = Session/Meta/AudioFiles/message_sent.aiff; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -1099,7 +1079,6 @@
|
|||
34D5CCA71EAE3D30005515DB /* AvatarViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvatarViewHelper.h; sourceTree = "<group>"; };
|
||||
34D5CCA81EAE3D30005515DB /* AvatarViewHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AvatarViewHelper.m; sourceTree = "<group>"; };
|
||||
34D99CE3217509C1000AFB39 /* AppEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppEnvironment.swift; sourceTree = "<group>"; };
|
||||
34E3E5671EC4B19400495BAC /* AudioProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioProgressView.swift; sourceTree = "<group>"; };
|
||||
34F308A01ECB469700BB7697 /* OWSBezierPathView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBezierPathView.h; sourceTree = "<group>"; };
|
||||
34F308A11ECB469700BB7697 /* OWSBezierPathView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBezierPathView.m; sourceTree = "<group>"; };
|
||||
4503F1BB20470A5B00CEE724 /* classic-quiet.aifc */ = {isa = PBXFileReference; lastKnownFileType = file; path = "classic-quiet.aifc"; sourceTree = "<group>"; };
|
||||
|
@ -1119,7 +1098,6 @@
|
|||
455A16DC1F1FEA0000F86704 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; };
|
||||
45847E861E4283C30080EAB3 /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; };
|
||||
45A2F004204473A3002E978A /* NewMessage.aifc */ = {isa = PBXFileReference; lastKnownFileType = file; name = NewMessage.aifc; path = Session/Meta/AudioFiles/NewMessage.aifc; sourceTree = SOURCE_ROOT; };
|
||||
45A6DAD51EBBF85500893231 /* ReminderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReminderView.swift; sourceTree = "<group>"; };
|
||||
45B201741DAECBFD00C461E0 /* Signal-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Signal-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
45B5360D206DD8BB00D61655 /* UIResponder+OWS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIResponder+OWS.swift"; sourceTree = "<group>"; };
|
||||
45B74A5B2044AAB300CD42F8 /* aurora-quiet.aifc */ = {isa = PBXFileReference; lastKnownFileType = file; path = "aurora-quiet.aifc"; sourceTree = "<group>"; };
|
||||
|
@ -1151,7 +1129,6 @@
|
|||
45C0DC1D1E69011F00E04C47 /* UIStoryboard+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard+OWS.swift"; sourceTree = "<group>"; };
|
||||
45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = "Launch Screen.storyboard"; path = "Session/Meta/Launch Screen.storyboard"; sourceTree = SOURCE_ROOT; };
|
||||
45CD81EE1DC030E7004C9430 /* SyncPushTokensJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncPushTokensJob.swift; sourceTree = "<group>"; };
|
||||
45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarqueeLabel.swift; sourceTree = "<group>"; };
|
||||
45F32C1D205718B000A300D5 /* MediaPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MediaPageViewController.swift; path = "Session/Media Viewing & Editing/MediaPageViewController.swift"; sourceTree = SOURCE_ROOT; };
|
||||
48AD214D67ABED845101E795 /* Pods_GlobalDependencies_Session.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GlobalDependencies_Session.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4C090A1A210FD9C7001FD7F9 /* HapticFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticFeedback.swift; sourceTree = "<group>"; };
|
||||
|
@ -1159,7 +1136,6 @@
|
|||
4C1D2337218B6BA000A0598F /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
4C21D5D7223AC60F00EF8A77 /* PhotoCapture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoCapture.swift; sourceTree = "<group>"; };
|
||||
4C4AE69F224AF21900D4AF6F /* SendMediaNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMediaNavigationController.swift; sourceTree = "<group>"; };
|
||||
4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DismissableTextField.swift; sourceTree = "<group>"; };
|
||||
4C586924224FAB83003FD070 /* AVAudioSession+OWS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AVAudioSession+OWS.h"; sourceTree = "<group>"; };
|
||||
4C586925224FAB83003FD070 /* AVAudioSession+OWS.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "AVAudioSession+OWS.m"; sourceTree = "<group>"; };
|
||||
4C63CBFF210A620B003AE45C /* SignalTSan.supp */ = {isa = PBXFileReference; lastKnownFileType = text; path = SignalTSan.supp; sourceTree = "<group>"; };
|
||||
|
@ -1233,8 +1209,8 @@
|
|||
7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAEScreenLockViewController.swift; sourceTree = "<group>"; };
|
||||
7BAF54D527ACD0E2003D12F8 /* ReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = "<group>"; };
|
||||
7BAF54DB27ACD12B003D12F8 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; };
|
||||
7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentTitleViewController.swift; sourceTree = "<group>"; };
|
||||
7BBBDC43286EAD2D00747E59 /* TappableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TappableLabel.swift; sourceTree = "<group>"; };
|
||||
7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentTitleViewController.swift; sourceTree = "<group>"; };
|
||||
7BC01A3B241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SessionNotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7BC01A3D241F40AB00BC7C55 /* NotificationServiceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtension.swift; sourceTree = "<group>"; };
|
||||
7BC01A3F241F40AB00BC7C55 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
@ -1377,7 +1353,6 @@
|
|||
C300A5F12554B09800555489 /* MessageSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSender.swift; sourceTree = "<group>"; };
|
||||
C300A5FB2554B0A000555489 /* MessageReceiver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageReceiver.swift; sourceTree = "<group>"; };
|
||||
C302093D25DCBF07001F572D /* MentionSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionSelectionView.swift; sourceTree = "<group>"; };
|
||||
C31A6C59247F214E001123EF /* UIView+Glow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Glow.swift"; sourceTree = "<group>"; };
|
||||
C31A6C5B247F2CF3001123EF /* CGRect+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGRect+Utilities.swift"; sourceTree = "<group>"; };
|
||||
C31D1DDC25217014005D4DA8 /* UserCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserCell.swift; sourceTree = "<group>"; };
|
||||
C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSelectionVC.swift; sourceTree = "<group>"; };
|
||||
|
@ -1388,7 +1363,6 @@
|
|||
C328254825CA60E60062D0A7 /* ContextMenuVC+Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContextMenuVC+Action.swift"; sourceTree = "<group>"; };
|
||||
C328255125CA64470062D0A7 /* ContextMenuVC+ActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContextMenuVC+ActionView.swift"; sourceTree = "<group>"; };
|
||||
C32C5A87256DBCF9003C73A2 /* MessageReceiver+ClosedGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageReceiver+ClosedGroups.swift"; sourceTree = "<group>"; };
|
||||
C33100132558FFC200070591 /* UIImage+Tinting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Tinting.swift"; sourceTree = "<group>"; };
|
||||
C33100272559000A00070591 /* UIView+Rendering.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Rendering.swift"; sourceTree = "<group>"; };
|
||||
C3310032255900A400070591 /* Notification+AppMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+AppMode.swift"; sourceTree = "<group>"; };
|
||||
C331FF1B2558F9D300070591 /* SessionUIKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SessionUIKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -1491,12 +1465,10 @@
|
|||
C38EF224255B6D5D007E1867 /* SignalAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SignalAttachment.swift; path = "SessionMessagingKit/Sending & Receiving/Attachments/SignalAttachment.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF226255B6D5D007E1867 /* ShareViewDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShareViewDelegate.swift; path = SignalUtilitiesKit/Utilities/ShareViewDelegate.swift; sourceTree = SOURCE_ROOT; };
|
||||
C38EF227255B6D5D007E1867 /* OWSVideoPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSVideoPlayer.swift; path = "SignalUtilitiesKit/Media Viewing & Editing/OWSVideoPlayer.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF236255B6D65007E1867 /* UIViewController+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIViewController+OWS.h"; path = "SignalUtilitiesKit/Utilities/UIViewController+OWS.h"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF237255B6D65007E1867 /* UIDevice+featureSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIDevice+featureSupport.swift"; path = "SessionUtilitiesKit/General/UIDevice+featureSupport.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF238255B6D66007E1867 /* UIFont+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIFont+OWS.m"; path = "SignalUtilitiesKit/Utilities/UIFont+OWS.m"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF239255B6D66007E1867 /* UIFont+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIFont+OWS.h"; path = "SignalUtilitiesKit/Utilities/UIFont+OWS.h"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF23A255B6D66007E1867 /* NSAttributedString+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+OWS.m"; path = "SignalUtilitiesKit/Utilities/NSAttributedString+OWS.m"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF23B255B6D66007E1867 /* UIViewController+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+OWS.m"; path = "SignalUtilitiesKit/Utilities/UIViewController+OWS.m"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF23C255B6D66007E1867 /* UIColor+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIColor+OWS.h"; path = "SignalUtilitiesKit/Utilities/UIColor+OWS.h"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF23D255B6D66007E1867 /* UIView+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+OWS.h"; path = "SessionUtilitiesKit/General/UIView+OWS.h"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF23E255B6D66007E1867 /* UIView+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+OWS.m"; path = "SessionUtilitiesKit/General/UIView+OWS.m"; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -1514,20 +1486,16 @@
|
|||
C38EF2EC255B6DBA007E1867 /* ProximityMonitoringManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProximityMonitoringManager.swift; path = SessionMessagingKit/Utilities/ProximityMonitoringManager.swift; sourceTree = SOURCE_ROOT; };
|
||||
C38EF2EF255B6DBB007E1867 /* Weak.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Weak.swift; path = SessionUtilitiesKit/General/Weak.swift; sourceTree = SOURCE_ROOT; };
|
||||
C38EF2F2255B6DBC007E1867 /* Searcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Searcher.swift; path = SignalUtilitiesKit/Utilities/Searcher.swift; sourceTree = SOURCE_ROOT; };
|
||||
C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImage+OWS.swift"; path = "SignalUtilitiesKit/Utilities/UIImage+OWS.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF2F5255B6DBC007E1867 /* OWSAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAudioPlayer.h; path = SessionMessagingKit/Utilities/OWSAudioPlayer.h; sourceTree = SOURCE_ROOT; };
|
||||
C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSAudioPlayer.m; path = SessionMessagingKit/Utilities/OWSAudioPlayer.m; sourceTree = SOURCE_ROOT; };
|
||||
C38EF2FA255B6DBD007E1867 /* Bench.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Bench.swift; path = SignalUtilitiesKit/Utilities/Bench.swift; sourceTree = SOURCE_ROOT; };
|
||||
C38EF2FB255B6DBD007E1867 /* OWSWindowManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSWindowManager.h; path = SessionMessagingKit/Utilities/OWSWindowManager.h; sourceTree = SOURCE_ROOT; };
|
||||
C38EF300255B6DBD007E1867 /* UIUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UIUtil.m; path = SignalUtilitiesKit/Utilities/UIUtil.m; sourceTree = SOURCE_ROOT; };
|
||||
C38EF301255B6DBD007E1867 /* OWSFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSFormat.h; path = SignalUtilitiesKit/Utilities/OWSFormat.h; sourceTree = SOURCE_ROOT; };
|
||||
C38EF304255B6DBE007E1867 /* ImageCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageCache.swift; path = SignalUtilitiesKit/Utilities/ImageCache.swift; sourceTree = SOURCE_ROOT; };
|
||||
C38EF305255B6DBE007E1867 /* OWSFormat.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSFormat.m; path = SignalUtilitiesKit/Utilities/OWSFormat.m; sourceTree = SOURCE_ROOT; };
|
||||
C38EF306255B6DBE007E1867 /* OWSWindowManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSWindowManager.m; path = SessionMessagingKit/Utilities/OWSWindowManager.m; sourceTree = SOURCE_ROOT; };
|
||||
C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIGestureRecognizer+OWS.swift"; path = "SignalUtilitiesKit/Utilities/UIGestureRecognizer+OWS.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF309255B6DBE007E1867 /* DeviceSleepManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DeviceSleepManager.swift; path = SessionMessagingKit/Utilities/DeviceSleepManager.swift; sourceTree = SOURCE_ROOT; };
|
||||
C38EF30A255B6DBE007E1867 /* UIUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UIUtil.h; path = SignalUtilitiesKit/Utilities/UIUtil.h; sourceTree = SOURCE_ROOT; };
|
||||
C38EF33F255B6DC5007E1867 /* SheetViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SheetViewController.swift; path = "SignalUtilitiesKit/Shared View Controllers/SheetViewController.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF343255B6DC5007E1867 /* OWSNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSNavigationController.h; path = "SignalUtilitiesKit/Shared View Controllers/OWSNavigationController.h"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF344255B6DC5007E1867 /* OWSViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSViewController.h; path = "SignalUtilitiesKit/Shared View Controllers/OWSViewController.h"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF349255B6DC7007E1867 /* ModalActivityIndicatorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ModalActivityIndicatorViewController.swift; path = "SignalUtilitiesKit/Shared View Controllers/ModalActivityIndicatorViewController.swift"; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -1542,7 +1510,6 @@
|
|||
C38EF381255B6DD1007E1867 /* AttachmentCaptionToolbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentCaptionToolbar.swift; path = "SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentCaptionToolbar.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF382255B6DD1007E1867 /* AttachmentPrepViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentPrepViewController.swift; path = "SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentPrepViewController.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF383255B6DD1007E1867 /* ApprovalRailCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ApprovalRailCellView.swift; path = "SignalUtilitiesKit/Shared Views/ApprovalRailCellView.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF384255B6DD2007E1867 /* AttachmentCaptionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentCaptionViewController.swift; path = "SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentCaptionViewController.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF3A8255B6DE4007E1867 /* ImageEditorTextViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorTextViewController.swift; path = "SignalUtilitiesKit/Media Viewing & Editing/Image Editing/ImageEditorTextViewController.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF3A9255B6DE4007E1867 /* ImageEditorPinchGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorPinchGestureRecognizer.swift; path = "SignalUtilitiesKit/Media Viewing & Editing/Image Editing/ImageEditorPinchGestureRecognizer.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF3AA255B6DE4007E1867 /* ImageEditorItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorItem.swift; path = "SignalUtilitiesKit/Media Viewing & Editing/Image Editing/ImageEditorItem.swift"; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -1573,7 +1540,6 @@
|
|||
C38EF3E4255B6DF4007E1867 /* CommonStrings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CommonStrings.swift; path = SignalUtilitiesKit/Utilities/CommonStrings.swift; sourceTree = SOURCE_ROOT; };
|
||||
C38EF3E7255B6DF5007E1867 /* OWSButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSButton.swift; path = "SignalUtilitiesKit/Shared Views/OWSButton.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF3E9255B6DF6007E1867 /* Toast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Toast.swift; path = "SignalUtilitiesKit/Shared Views/Toast.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF3EC255B6DF6007E1867 /* OWSFlatButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSFlatButton.swift; path = "SignalUtilitiesKit/Shared Views/OWSFlatButton.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF3ED255B6DF6007E1867 /* TappableStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TappableStackView.swift; path = "SignalUtilitiesKit/Shared Views/TappableStackView.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF3EE255B6DF6007E1867 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GradientView.swift; path = "SignalUtilitiesKit/Shared Views/GradientView.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF458255B710A007E1867 /* SignalUtilitiesKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SignalUtilitiesKit-Prefix.pch"; sourceTree = "<group>"; };
|
||||
|
@ -1649,7 +1615,6 @@
|
|||
C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCopyableLabel.swift; sourceTree = "<group>"; };
|
||||
C3DB66AB260ACA42001EFC55 /* OpenGroupManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupManager.swift; sourceTree = "<group>"; };
|
||||
C3DB66C2260ACCE6001EFC55 /* OpenGroupPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupPoller.swift; sourceTree = "<group>"; };
|
||||
C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sheet.swift; sourceTree = "<group>"; };
|
||||
C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditClosedGroupVC.swift; sourceTree = "<group>"; };
|
||||
C3ECBF7A257056B700EA7FCE /* Threading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Threading.swift; sourceTree = "<group>"; };
|
||||
C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoopNotificationsManager.swift; sourceTree = "<group>"; };
|
||||
|
@ -1822,6 +1787,9 @@
|
|||
FD71161628D00DA400B47552 /* ThreadSettingsViewModelSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadSettingsViewModelSpec.swift; sourceTree = "<group>"; };
|
||||
FD71161928D00E1100B47552 /* NotificationContentViewModelSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentViewModelSpec.swift; sourceTree = "<group>"; };
|
||||
FD71161B28D194FB00B47552 /* MentionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionInfo.swift; sourceTree = "<group>"; };
|
||||
FD71161D28D9772700B47552 /* UIViewController+OWS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+OWS.swift"; sourceTree = "<group>"; };
|
||||
FD71161F28D97ABC00B47552 /* UIImage+Tinting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Tinting.swift"; sourceTree = "<group>"; };
|
||||
FD71162128D983ED00B47552 /* QRCodeScanningViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeScanningViewController.swift; sourceTree = "<group>"; };
|
||||
FD7162DA281B6C440060647B /* TypedTableAlias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedTableAlias.swift; sourceTree = "<group>"; };
|
||||
FD716E6328502DDD00C96BF4 /* CallManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallManagerProtocol.swift; sourceTree = "<group>"; };
|
||||
FD716E6528502EE200C96BF4 /* CurrentCallProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentCallProtocol.swift; sourceTree = "<group>"; };
|
||||
|
@ -2247,7 +2215,6 @@
|
|||
B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */,
|
||||
FD1C98E3282E3C5B00B76F9E /* UINavigationBar+Utilities.swift */,
|
||||
FD848B8E283EF2A8000E298B /* UIScrollView+Utilities.swift */,
|
||||
C31A6C59247F214E001123EF /* UIView+Glow.swift */,
|
||||
C3548F0724456AB6009433A8 /* UIView+Wrapping.swift */,
|
||||
7BAADFCD27B215FE007BCF92 /* UIView+Draggable.swift */,
|
||||
7BFD1A892745C4F000FB91B9 /* Permissions.swift */,
|
||||
|
@ -2589,19 +2556,12 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
4CA46F4B219CCC630038ABDE /* CaptionView.swift */,
|
||||
4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */,
|
||||
45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */,
|
||||
34386A53207D271C009F5D9C /* NeverClearView.swift */,
|
||||
34F308A01ECB469700BB7697 /* OWSBezierPathView.h */,
|
||||
34F308A11ECB469700BB7697 /* OWSBezierPathView.m */,
|
||||
34330AA11E79686200DF2FB9 /* OWSProgressView.h */,
|
||||
34330AA21E79686200DF2FB9 /* OWSProgressView.m */,
|
||||
45A6DAD51EBBF85500893231 /* ReminderView.swift */,
|
||||
C354E75923FE2A7600CE22E3 /* BaseVC.swift */,
|
||||
B8BB82AA238F669C00BA5194 /* FullConversationCell.swift */,
|
||||
4542DF53208D40AC007B4E76 /* LoadingViewController.swift */,
|
||||
340FC888204DAC8C007AEB0F /* OWSQRCodeScanningViewController.h */,
|
||||
340FC896204DAC8C007AEB0F /* OWSQRCodeScanningViewController.m */,
|
||||
FD71162128D983ED00B47552 /* QRCodeScanningViewController.swift */,
|
||||
B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */,
|
||||
C31D1DDC25217014005D4DA8 /* UserCell.swift */,
|
||||
C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */,
|
||||
|
@ -2841,9 +2801,9 @@
|
|||
children = (
|
||||
B8544E3023D16CA500299F14 /* DeviceUtilities.swift */,
|
||||
FD37E9D628A20B5D003AE748 /* UIColor+Utilities.swift */,
|
||||
C33100132558FFC200070591 /* UIImage+Tinting.swift */,
|
||||
B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */,
|
||||
C33100272559000A00070591 /* UIView+Rendering.swift */,
|
||||
FD71161F28D97ABC00B47552 /* UIImage+Tinting.swift */,
|
||||
);
|
||||
path = Utilities;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2990,7 +2950,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
B86BD08323399ACF000F5AE3 /* Modal.swift */,
|
||||
C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */,
|
||||
FD52090628B49738006098F6 /* ConfirmationModal.swift */,
|
||||
);
|
||||
path = "Sheets & Modals";
|
||||
|
@ -3019,7 +2978,6 @@
|
|||
45F32C1D205718B000A300D5 /* MediaPageViewController.swift */,
|
||||
454A84032059C787008B8C75 /* MediaTileViewController.swift */,
|
||||
7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */,
|
||||
34E3E5671EC4B19400495BAC /* AudioProgressView.swift */,
|
||||
346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */,
|
||||
34969559219B605E00DCFE74 /* ImagePickerController.swift */,
|
||||
34A6C27F21E503E600B5B12E /* OWSImagePickerController.swift */,
|
||||
|
@ -3085,7 +3043,6 @@
|
|||
C38EF356255B6DCB007E1867 /* OWSNavigationController.m */,
|
||||
C38EF344255B6DC5007E1867 /* OWSViewController.h */,
|
||||
C38EF355255B6DCB007E1867 /* OWSViewController.m */,
|
||||
C38EF33F255B6DC5007E1867 /* SheetViewController.swift */,
|
||||
);
|
||||
path = "Shared View Controllers";
|
||||
sourceTree = "<group>";
|
||||
|
@ -3127,7 +3084,6 @@
|
|||
C38EF37D255B6DCF007E1867 /* AttachmentApprovalInputAccessoryView.swift */,
|
||||
C38EF37F255B6DD0007E1867 /* AttachmentApprovalViewController.swift */,
|
||||
C38EF381255B6DD1007E1867 /* AttachmentCaptionToolbar.swift */,
|
||||
C38EF384255B6DD2007E1867 /* AttachmentCaptionViewController.swift */,
|
||||
C38EF37E255B6DD0007E1867 /* AttachmentItemCollection.swift */,
|
||||
C38EF382255B6DD1007E1867 /* AttachmentPrepViewController.swift */,
|
||||
C38EF37C255B6DCF007E1867 /* AttachmentTextToolbar.swift */,
|
||||
|
@ -3142,7 +3098,6 @@
|
|||
B8C2B2C72563685C00551B4D /* CircleView.swift */,
|
||||
C38EF383255B6DD1007E1867 /* ApprovalRailCellView.swift */,
|
||||
C38EF3E7255B6DF5007E1867 /* OWSButton.swift */,
|
||||
C38EF3EC255B6DF6007E1867 /* OWSFlatButton.swift */,
|
||||
C38EF3DB255B6DF1007E1867 /* OWSLayerView.swift */,
|
||||
C38EF3D9255B6DF1007E1867 /* OWSNavigationBar.swift */,
|
||||
C38EF3D7255B6DF0007E1867 /* OWSTextField.h */,
|
||||
|
@ -3358,16 +3313,12 @@
|
|||
7BAF54DB27ACD12B003D12F8 /* UIColor+Extensions.swift */,
|
||||
C38EF3DC255B6DF1007E1867 /* DirectionalPanGestureRecognizer.swift */,
|
||||
C38EF240255B6D67007E1867 /* UIView+OWS.swift */,
|
||||
C38EF236255B6D65007E1867 /* UIViewController+OWS.h */,
|
||||
C38EF23B255B6D66007E1867 /* UIViewController+OWS.m */,
|
||||
FD71161D28D9772700B47552 /* UIViewController+OWS.swift */,
|
||||
C38EF23C255B6D66007E1867 /* UIColor+OWS.h */,
|
||||
C38EF242255B6D67007E1867 /* UIColor+OWS.m */,
|
||||
C38EF2B2255B6D9C007E1867 /* UIView+Utilities.swift */,
|
||||
C38EF2B1255B6D9C007E1867 /* UIViewController+Utilities.swift */,
|
||||
C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */,
|
||||
C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */,
|
||||
C38EF30A255B6DBE007E1867 /* UIUtil.h */,
|
||||
C38EF300255B6DBD007E1867 /* UIUtil.m */,
|
||||
C38EF239255B6D66007E1867 /* UIFont+OWS.h */,
|
||||
C38EF238255B6D66007E1867 /* UIFont+OWS.m */,
|
||||
C33FDA96255A57FE00E217F9 /* OWSDispatch.h */,
|
||||
|
@ -4213,7 +4164,6 @@
|
|||
files = (
|
||||
C33FDDB0255A582000E217F9 /* NSURLSessionDataTask+StatusCode.h in Headers */,
|
||||
C33FDDD0255A582000E217F9 /* FunctionalUtil.h in Headers */,
|
||||
C38EF334255B6DBF007E1867 /* UIUtil.h in Headers */,
|
||||
C33FDD5B255A582000E217F9 /* OWSOperation.h in Headers */,
|
||||
C33FDD7C255A582000E217F9 /* SSKAsserts.h in Headers */,
|
||||
C38EF3F6255B6DF7007E1867 /* OWSTextView.h in Headers */,
|
||||
|
@ -4221,7 +4171,6 @@
|
|||
C38EF32B255B6DBF007E1867 /* OWSFormat.h in Headers */,
|
||||
C33FDDCC255A582000E217F9 /* TSConstants.h in Headers */,
|
||||
C33FDDBD255A582000E217F9 /* ByteParser.h in Headers */,
|
||||
C38EF243255B6D67007E1867 /* UIViewController+OWS.h in Headers */,
|
||||
C38EF35D255B6DCC007E1867 /* OWSNavigationController.h in Headers */,
|
||||
C38EF249255B6D67007E1867 /* UIColor+OWS.h in Headers */,
|
||||
C38EF3F5255B6DF7007E1867 /* OWSTextField.h in Headers */,
|
||||
|
@ -5192,13 +5141,13 @@
|
|||
C331FF982558FA6B00070591 /* AppMode.swift in Sources */,
|
||||
FD52090328B4680F006098F6 /* RadioButton.swift in Sources */,
|
||||
C331FFE82558FB0000070591 /* TextView.swift in Sources */,
|
||||
FD71162028D97ABC00B47552 /* UIImage+Tinting.swift in Sources */,
|
||||
FD37E9D728A20B5D003AE748 /* UIColor+Utilities.swift in Sources */,
|
||||
FD37E9F928A5F14A003AE748 /* _001_ThemePreferences.swift in Sources */,
|
||||
FD37E9C328A1C6F3003AE748 /* ThemeManager.swift in Sources */,
|
||||
C331FF9A2558FA6B00070591 /* Values.swift in Sources */,
|
||||
FD37E9C628A1D4EC003AE748 /* Theme+ClassicDark.swift in Sources */,
|
||||
C331FFE42558FB0000070591 /* OutlineButton.swift in Sources */,
|
||||
C33100142558FFC200070591 /* UIImage+Tinting.swift in Sources */,
|
||||
C3310033255900A400070591 /* Notification+AppMode.swift in Sources */,
|
||||
C331FFE92558FB0000070591 /* Separator.swift in Sources */,
|
||||
C33100282559000A00070591 /* UIView+Rendering.swift in Sources */,
|
||||
|
@ -5220,6 +5169,7 @@
|
|||
C38EF2A5255B6D93007E1867 /* Identicon+ObjC.swift in Sources */,
|
||||
C38EF385255B6DD2007E1867 /* AttachmentTextToolbar.swift in Sources */,
|
||||
C33FDD23255A582000E217F9 /* FeatureFlags.swift in Sources */,
|
||||
FD71161E28D9772700B47552 /* UIViewController+OWS.swift in Sources */,
|
||||
C38EF389255B6DD2007E1867 /* AttachmentTextView.swift in Sources */,
|
||||
C38EF3FF255B6DF7007E1867 /* TappableView.swift in Sources */,
|
||||
C38EF3C2255B6DE7007E1867 /* ImageEditorPaletteView.swift in Sources */,
|
||||
|
@ -5230,7 +5180,6 @@
|
|||
C38EF30C255B6DBF007E1867 /* ScreenLock.swift in Sources */,
|
||||
C38EF363255B6DCC007E1867 /* ModalActivityIndicatorViewController.swift in Sources */,
|
||||
C38EF38A255B6DD2007E1867 /* AttachmentCaptionToolbar.swift in Sources */,
|
||||
C38EF40A255B6DF7007E1867 /* OWSFlatButton.swift in Sources */,
|
||||
C33FDCD1255A582000E217F9 /* FunctionalUtil.m in Sources */,
|
||||
C38EF402255B6DF7007E1867 /* CommonStrings.swift in Sources */,
|
||||
C38EF3C1255B6DE7007E1867 /* ImageEditorBrushViewController.swift in Sources */,
|
||||
|
@ -5243,7 +5192,6 @@
|
|||
C33FDC29255A581F00E217F9 /* ReachabilityManager.swift in Sources */,
|
||||
C38EF407255B6DF7007E1867 /* Toast.swift in Sources */,
|
||||
C38EF38C255B6DD2007E1867 /* ApprovalRailCellView.swift in Sources */,
|
||||
C38EF32A255B6DBF007E1867 /* UIUtil.m in Sources */,
|
||||
C38EF2A6255B6D93007E1867 /* PlaceholderIcon.swift in Sources */,
|
||||
C33FDD92255A582000E217F9 /* SignalIOS.pb.swift in Sources */,
|
||||
C33FDC45255A581F00E217F9 /* AppVersion.m in Sources */,
|
||||
|
@ -5254,7 +5202,6 @@
|
|||
FD87DD0428B8727D00AF0F98 /* Configuration.swift in Sources */,
|
||||
C38EF3BA255B6DE7007E1867 /* ImageEditorItem.swift in Sources */,
|
||||
C38EF3F7255B6DF7007E1867 /* OWSNavigationBar.swift in Sources */,
|
||||
C38EF248255B6D67007E1867 /* UIViewController+OWS.m in Sources */,
|
||||
C33FDD3A255A582000E217F9 /* Notification+Loki.swift in Sources */,
|
||||
7BAF54DC27ACD12B003D12F8 /* UIColor+Extensions.swift in Sources */,
|
||||
C38EF370255B6DCC007E1867 /* OWSNavigationController.m in Sources */,
|
||||
|
@ -5291,14 +5238,11 @@
|
|||
C33FDC98255A582000E217F9 /* SwiftSingletons.swift in Sources */,
|
||||
C38EF3B8255B6DE7007E1867 /* ImageEditorTextViewController.swift in Sources */,
|
||||
C38EF40B255B6DF7007E1867 /* TappableStackView.swift in Sources */,
|
||||
C38EF31D255B6DBF007E1867 /* UIImage+OWS.swift in Sources */,
|
||||
C38EF359255B6DCC007E1867 /* SheetViewController.swift in Sources */,
|
||||
C38EF386255B6DD2007E1867 /* AttachmentApprovalInputAccessoryView.swift in Sources */,
|
||||
B8C2B2C82563685C00551B4D /* CircleView.swift in Sources */,
|
||||
C38EF331255B6DBF007E1867 /* UIGestureRecognizer+OWS.swift in Sources */,
|
||||
C33FDDC5255A582000E217F9 /* OWSError.m in Sources */,
|
||||
FD848B9C284435D7000E298B /* AppSetup.swift in Sources */,
|
||||
C38EF38D255B6DD2007E1867 /* AttachmentCaptionViewController.swift in Sources */,
|
||||
C38EF31C255B6DBF007E1867 /* Searcher.swift in Sources */,
|
||||
C38EF2B3255B6D9C007E1867 /* UIViewController+Utilities.swift in Sources */,
|
||||
C38EF3BE255B6DE7007E1867 /* OrderedDictionary.swift in Sources */,
|
||||
|
@ -5668,7 +5612,6 @@
|
|||
B83524A525C3BA4B0089A44F /* InfoMessageCell.swift in Sources */,
|
||||
7B9F71D82853100A006DFE7B /* EmojiWithSkinTones.swift in Sources */,
|
||||
B84A89BC25DE328A0040017D /* ProfilePictureVC.swift in Sources */,
|
||||
34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */,
|
||||
FDCDB8E02811007F00352A0C /* HomeViewModel.swift in Sources */,
|
||||
7B0EFDF62755CC5400FFAAE7 /* CallMissedTipsModal.swift in Sources */,
|
||||
C374EEF425DB31D40073A857 /* VoiceMessageRecordingView.swift in Sources */,
|
||||
|
@ -5682,7 +5625,6 @@
|
|||
34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */,
|
||||
34A8B3512190A40E00218A25 /* MediaAlbumView.swift in Sources */,
|
||||
FD09C5E828264937000CE219 /* MediaDetailViewController.swift in Sources */,
|
||||
4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */,
|
||||
3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */,
|
||||
7B1B52E028580D51006069F2 /* EmojiSkinTonePicker.swift in Sources */,
|
||||
B849789625D4A2F500D0D0B3 /* LinkPreviewView.swift in Sources */,
|
||||
|
@ -5693,7 +5635,6 @@
|
|||
7BA68909272A27BE00EFC32F /* SessionCall.swift in Sources */,
|
||||
B835247925C38D880089A44F /* MessageCell.swift in Sources */,
|
||||
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */,
|
||||
34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */,
|
||||
B8D0A26925E4A2C200C1835E /* Onboarding.swift in Sources */,
|
||||
34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */,
|
||||
4CC613362227A00400E21A3A /* ConversationSearch.swift in Sources */,
|
||||
|
@ -5709,17 +5650,14 @@
|
|||
7BA6890F27325CE300EFC32F /* SessionCallManager+CXProvider.swift in Sources */,
|
||||
34BECE301F7ABCF800D7438D /* GifPickerLayout.swift in Sources */,
|
||||
7B46AAAF28766DF4001AF2DC /* AllMediaViewController.swift in Sources */,
|
||||
FD71162228D983ED00B47552 /* QRCodeScanningViewController.swift in Sources */,
|
||||
C331FFFE2558FF3B00070591 /* FullConversationCell.swift in Sources */,
|
||||
FD52090728B49738006098F6 /* ConfirmationModal.swift in Sources */,
|
||||
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */,
|
||||
B8D84EA325DF745A005A043E /* LinkPreviewState.swift in Sources */,
|
||||
45C0DC1E1E69011F00E04C47 /* UIStoryboard+OWS.swift in Sources */,
|
||||
45A6DAD61EBBF85500893231 /* ReminderView.swift in Sources */,
|
||||
B82B408E239DC00D00A248E7 /* DisplayNameVC.swift in Sources */,
|
||||
B835246E25C38ABF0089A44F /* ConversationVC.swift in Sources */,
|
||||
7B7037432834B81F000DCF35 /* ReactionContainerView.swift in Sources */,
|
||||
B8AF4BB426A5204600583500 /* SendSeedModal.swift in Sources */,
|
||||
B821494625D4D6FF009C0F2A /* URLModal.swift in Sources */,
|
||||
7BBBDC462875600700747E59 /* DocumentTitleViewController.swift in Sources */,
|
||||
B877E24226CA12910007970A /* CallVC.swift in Sources */,
|
||||
7BA6890D27325CCC00EFC32F /* SessionCallManager+CXCallController.swift in Sources */,
|
||||
|
@ -5730,7 +5668,6 @@
|
|||
FD37EA1928AC5CCA003AE748 /* NotificationSoundViewModel.swift in Sources */,
|
||||
FD7115EE28C5D79B00B47552 /* SettingsAvatarCell.swift in Sources */,
|
||||
4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */,
|
||||
34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */,
|
||||
C328254925CA60E60062D0A7 /* ContextMenuVC+Action.swift in Sources */,
|
||||
4542DF54208D40AC007B4E76 /* LoadingViewController.swift in Sources */,
|
||||
34D5CCA91EAE3D30005515DB /* AvatarViewHelper.m in Sources */,
|
||||
|
@ -5794,14 +5731,12 @@
|
|||
FD4B200E283492210034334B /* InsetLockableTableView.swift in Sources */,
|
||||
B8269D3325C7A8C600488AB4 /* InputViewButton.swift in Sources */,
|
||||
B8269D3D25C7B34D00488AB4 /* InputTextView.swift in Sources */,
|
||||
340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */,
|
||||
7B0EFDF0275084AA00FFAAE7 /* CallMessageCell.swift in Sources */,
|
||||
FD37EA0B28AB12E2003AE748 /* SettingsCell.swift in Sources */,
|
||||
7B93D06A27CF173D00811CB6 /* MessageRequestsViewController.swift in Sources */,
|
||||
C33100082558FF6D00070591 /* NewConversationButtonSet.swift in Sources */,
|
||||
C3AAFFF225AE99710089E6DD /* AppDelegate.swift in Sources */,
|
||||
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */,
|
||||
C31A6C5A247F214E001123EF /* UIView+Glow.swift in Sources */,
|
||||
4521C3C01F59F3BA00B4C582 /* TextFieldHelper.swift in Sources */,
|
||||
FD37E9D128A1F2EB003AE748 /* ThemeSelectionView.swift in Sources */,
|
||||
7B9F71D22852EEE2006DFE7B /* Emoji+SkinTones.swift in Sources */,
|
||||
|
@ -5821,7 +5756,6 @@
|
|||
FDD2506E283711D600198BDA /* DifferenceKit+Utilities.swift in Sources */,
|
||||
B897621C25D201F7004F83B2 /* ScrollToBottomButton.swift in Sources */,
|
||||
346B66311F4E29B200E5122F /* CropScaleImageViewController.swift in Sources */,
|
||||
45E5A6991F61E6DE001E4A8A /* MarqueeLabel.swift in Sources */,
|
||||
FD1C98E4282E3C5B00B76F9E /* UINavigationBar+Utilities.swift in Sources */,
|
||||
C302093E25DCBF08001F572D /* MentionSelectionView.swift in Sources */,
|
||||
C328251F25CA3A900062D0A7 /* QuoteView.swift in Sources */,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright © 2021 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
import CoreMedia
|
||||
|
||||
class RenderView: UIView {
|
||||
|
|
|
@ -111,8 +111,8 @@ extension ContextMenuVC {
|
|||
else {
|
||||
if didTouchDownInside {
|
||||
themeBackgroundColor = .clear
|
||||
iconImageView.themeTintColor = .textPrimary
|
||||
titleLabel.themeTextColor = .textPrimary
|
||||
iconImageView.themeTintColor = .contextMenu_text
|
||||
titleLabel.themeTextColor = .contextMenu_text
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -125,8 +125,8 @@ extension ContextMenuVC {
|
|||
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
if didTouchDownInside {
|
||||
themeBackgroundColor = .clear
|
||||
iconImageView.themeTintColor = .textPrimary
|
||||
titleLabel.themeTextColor = .textPrimary
|
||||
iconImageView.themeTintColor = .contextMenu_text
|
||||
titleLabel.themeTextColor = .contextMenu_text
|
||||
}
|
||||
|
||||
didTouchDownInside = false
|
||||
|
@ -135,8 +135,8 @@ extension ContextMenuVC {
|
|||
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
if didTouchDownInside {
|
||||
themeBackgroundColor = .clear
|
||||
iconImageView.themeTintColor = .textPrimary
|
||||
titleLabel.themeTextColor = .textPrimary
|
||||
iconImageView.themeTintColor = .contextMenu_text
|
||||
titleLabel.themeTextColor = .contextMenu_text
|
||||
}
|
||||
|
||||
didTouchDownInside = false
|
||||
|
|
|
@ -340,17 +340,35 @@ final class ContextMenuVC: UIViewController {
|
|||
|
||||
func snDismiss() {
|
||||
let currentFrame: CGRect = self.snapshot.frame
|
||||
let currentLabelFrame: CGRect = self.timestampLabel.frame
|
||||
let originalFrame: CGRect = self.frame
|
||||
let frameDiff: CGRect = CGRect(
|
||||
x: (currentFrame.minX - originalFrame.minX),
|
||||
y: (currentFrame.minY - originalFrame.minY),
|
||||
width: (currentFrame.width - originalFrame.width),
|
||||
height: (currentFrame.height - originalFrame.height)
|
||||
)
|
||||
let endLabelFrame: CGRect = CGRect(
|
||||
x: (currentLabelFrame.minX - (frameDiff.origin.x + frameDiff.width)),
|
||||
y: (currentLabelFrame.minY - (frameDiff.origin.y + frameDiff.height)),
|
||||
width: currentLabelFrame.width,
|
||||
height: currentLabelFrame.height
|
||||
)
|
||||
|
||||
// Remove the snapshot view from the view hierarchy to remove its constaints (and prevent
|
||||
// them from causing animation bugs - also need to turn 'translatesAutoresizingMaskIntoConstraints'
|
||||
// back on so autod layout doesn't mess with the frame manipulation)
|
||||
// Remove the snapshot view and it's timestampLabel from the view hierarchy to remove its
|
||||
// constaints (and prevent them from causing animation bugs - also need to turn
|
||||
// 'translatesAutoresizingMaskIntoConstraints' back on so autod layout doesn't mess with
|
||||
// the frame manipulation)
|
||||
let oldSuperview: UIView? = self.snapshot.superview
|
||||
self.snapshot.removeFromSuperview()
|
||||
self.timestampLabel.removeFromSuperview()
|
||||
oldSuperview?.insertSubview(self.snapshot, aboveSubview: self.blurView)
|
||||
oldSuperview?.insertSubview(self.timestampLabel, aboveSubview: self.blurView)
|
||||
|
||||
self.snapshot.translatesAutoresizingMaskIntoConstraints = true
|
||||
self.timestampLabel.translatesAutoresizingMaskIntoConstraints = true
|
||||
self.snapshot.frame = currentFrame
|
||||
self.timestampLabel.frame = currentLabelFrame
|
||||
|
||||
UIView.animate(
|
||||
withDuration: 0.15,
|
||||
|
@ -358,6 +376,7 @@ final class ContextMenuVC: UIViewController {
|
|||
options: .curveEaseOut,
|
||||
animations: { [weak self] in
|
||||
self?.snapshot.frame = originalFrame
|
||||
self?.timestampLabel.frame = endLabelFrame
|
||||
},
|
||||
completion: nil
|
||||
)
|
||||
|
|
|
@ -126,43 +126,49 @@ public final class SearchResultsBar: UIView {
|
|||
private lazy var label: UILabel = {
|
||||
let result = UILabel()
|
||||
result.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
||||
result.textColor = Colors.text
|
||||
result.themeTextColor = .textPrimary
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var upButton: UIButton = {
|
||||
let icon = #imageLiteral(resourceName: "ic_chevron_up").withRenderingMode(.alwaysTemplate)
|
||||
let result = UIButton()
|
||||
let result: UIButton = UIButton()
|
||||
result.setImage(icon, for: UIControl.State.normal)
|
||||
result.tintColor = Colors.accent
|
||||
result.themeTintColor = .primary
|
||||
result.addTarget(self, action: #selector(handleUpButtonTapped), for: UIControl.Event.touchUpInside)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var downButton: UIButton = {
|
||||
let icon = #imageLiteral(resourceName: "ic_chevron_down").withRenderingMode(.alwaysTemplate)
|
||||
let result = UIButton()
|
||||
let result: UIButton = UIButton()
|
||||
result.setImage(icon, for: UIControl.State.normal)
|
||||
result.tintColor = Colors.accent
|
||||
result.themeTintColor = .primary
|
||||
result.addTarget(self, action: #selector(handleDownButtonTapped), for: UIControl.Event.touchUpInside)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var loadingIndicator: UIActivityIndicatorView = {
|
||||
let result = UIActivityIndicatorView(style: .medium)
|
||||
result.tintColor = Colors.text
|
||||
result.themeTintColor = .textPrimary
|
||||
result.alpha = 0.5
|
||||
result.hidesWhenStopped = true
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
|
@ -171,7 +177,7 @@ public final class SearchResultsBar: UIView {
|
|||
|
||||
// Background & blur
|
||||
let backgroundView = UIView()
|
||||
backgroundView.backgroundColor = isLightMode ? .white : .black
|
||||
backgroundView.themeBackgroundColor = .backgroundSecondary
|
||||
backgroundView.alpha = Values.lowOpacity
|
||||
addSubview(backgroundView)
|
||||
backgroundView.pin(to: self)
|
||||
|
@ -189,8 +195,8 @@ public final class SearchResultsBar: UIView {
|
|||
|
||||
// Separator
|
||||
let separator = UIView()
|
||||
separator.backgroundColor = Colors.text.withAlphaComponent(0.2)
|
||||
separator.set(.height, to: 1 / UIScreen.main.scale)
|
||||
separator.themeBackgroundColor = .borderSeparator
|
||||
separator.set(.height, to: Values.separatorThickness)
|
||||
addSubview(separator)
|
||||
separator.pin([ UIView.HorizontalEdge.leading, UIView.VerticalEdge.top, UIView.HorizontalEdge.trailing ], to: self)
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ extension ConversationVC:
|
|||
|
||||
// MARK: - SendMediaNavDelegate
|
||||
|
||||
func sendMediaNavDidCancel(_ sendMediaNavigationController: SendMediaNavigationController) {
|
||||
func sendMediaNavDidCancel(_ sendMediaNavigationController: SendMediaNavigationController?) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
@ -795,7 +795,7 @@ extension ConversationVC:
|
|||
)
|
||||
}
|
||||
|
||||
self.contextMenuWindow?.backgroundColor = .clear
|
||||
self.contextMenuWindow?.themeBackgroundColor = .clear
|
||||
self.contextMenuWindow?.rootViewController = self.contextMenuVC
|
||||
self.contextMenuWindow?.overrideUserInterfaceStyle = (isDarkMode ? .dark : .light)
|
||||
self.contextMenuWindow?.makeKeyAndVisible()
|
||||
|
|
|
@ -137,7 +137,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
|
|||
lazy var tableView: InsetLockableTableView = {
|
||||
let result: InsetLockableTableView = InsetLockableTableView()
|
||||
result.separatorStyle = .none
|
||||
result.backgroundColor = .clear
|
||||
result.themeBackgroundColor = .clear
|
||||
result.showsVerticalScrollIndicator = false
|
||||
result.contentInsetAdjustmentBehavior = .never
|
||||
result.keyboardDismissMode = .interactive
|
||||
|
@ -257,7 +257,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
|
|||
result.clipsToBounds = true
|
||||
result.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
|
||||
result.setTitle("TXT_BLOCK_USER_TITLE".localized(), for: .normal)
|
||||
result.setTitleColor(Colors.destructive, for: .normal)
|
||||
result.setThemeTitleColor(.danger, for: .normal)
|
||||
result.addTarget(self, action: #selector(block), for: .touchUpInside)
|
||||
|
||||
return result
|
||||
|
@ -1058,7 +1058,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
|
|||
self.view.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let keyboardTop = (UIScreen.main.bounds.height - keyboardRect.minY)
|
||||
let messageRequestsOffset: CGFloat = (messageRequestView.isHidden ? 0 : messageRequestView.bounds.height + 16)
|
||||
let oldContentInset: UIEdgeInsets = tableView.contentInset
|
||||
|
@ -1210,7 +1210,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
|
|||
switch section.model {
|
||||
case .loadOlder, .loadNewer:
|
||||
let loadingIndicator: UIActivityIndicatorView = UIActivityIndicatorView(style: .medium)
|
||||
loadingIndicator.tintColor = Colors.text
|
||||
loadingIndicator.themeTintColor = .textPrimary
|
||||
loadingIndicator.alpha = 0.5
|
||||
loadingIndicator.startAnimating()
|
||||
|
||||
|
@ -1386,7 +1386,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
|
|||
let ipadCancelButton = UIButton()
|
||||
ipadCancelButton.setTitle("cancel".localized(), for: .normal)
|
||||
ipadCancelButton.addTarget(self, action: #selector(hideSearchUI), for: .touchUpInside)
|
||||
ipadCancelButton.setTitleColor(Colors.text, for: .normal)
|
||||
ipadCancelButton.setThemeTitleColor(.textPrimary, for: .normal)
|
||||
searchBarContainer.addSubview(ipadCancelButton)
|
||||
ipadCancelButton.pin(.trailing, to: .trailing, of: searchBarContainer)
|
||||
ipadCancelButton.autoVCenterInSuperview()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
|
||||
class EmojiSkinTonePicker: UIView {
|
||||
let emoji: Emoji
|
||||
|
@ -116,12 +118,12 @@ class EmojiSkinTonePicker: UIView {
|
|||
layer.shadowOpacity = 0.25
|
||||
layer.shadowRadius = 4
|
||||
|
||||
referenceOverlay.backgroundColor = Colors.modalBackground
|
||||
referenceOverlay.themeBackgroundColor = .backgroundSecondary
|
||||
referenceOverlay.layer.cornerRadius = 9
|
||||
addSubview(referenceOverlay)
|
||||
|
||||
containerView.layoutMargins = UIEdgeInsets(top: 9, leading: 16, bottom: 9, trailing: 16)
|
||||
containerView.backgroundColor = Colors.modalBackground
|
||||
containerView.themeBackgroundColor = .backgroundSecondary
|
||||
containerView.layer.cornerRadius = 11
|
||||
addSubview(containerView)
|
||||
containerView.autoPinWidthToSuperview()
|
||||
|
@ -129,7 +131,8 @@ class EmojiSkinTonePicker: UIView {
|
|||
|
||||
if emoji.baseEmoji!.allowsMultipleSkinTones {
|
||||
prepareForMultipleSkinTones()
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
prepareForSingleSkinTone()
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +162,7 @@ class EmojiSkinTonePicker: UIView {
|
|||
|
||||
let divider = UIView()
|
||||
divider.autoSetDimension(.width, toSize: 1)
|
||||
divider.backgroundColor = isDarkMode ? .ows_gray75 : .ows_gray05
|
||||
divider.themeBackgroundColor = .borderSeparator
|
||||
hStack.addArrangedSubview(divider)
|
||||
|
||||
hStack.addArrangedSubview(.spacer(withWidth: 2))
|
||||
|
@ -266,7 +269,7 @@ class EmojiSkinTonePicker: UIView {
|
|||
|
||||
let divider = UIView()
|
||||
divider.autoSetDimension(.height, toSize: 1)
|
||||
divider.backgroundColor = isDarkMode ? .ows_gray75 : .ows_gray05
|
||||
divider.themeBackgroundColor = .borderSeparator
|
||||
vStack.addArrangedSubview(divider)
|
||||
|
||||
let leftSpacer = UIView.hStretchingSpacer()
|
||||
|
@ -296,7 +299,7 @@ class EmojiSkinTonePicker: UIView {
|
|||
let button = OWSButton { handler(emoji) }
|
||||
button.titleLabel?.font = .boldSystemFont(ofSize: 32)
|
||||
button.setTitle(emoji.rawValue, for: .normal)
|
||||
button.setBackgroundImage(UIImage(color: isDarkMode ? .ows_gray60 : .ows_gray25), for: .selected)
|
||||
button.setThemeBackgroundColor(.backgroundPrimary, for: .selected)
|
||||
button.layer.cornerRadius = 6
|
||||
button.clipsToBounds = true
|
||||
button.autoSetDimensions(to: CGSize(width: 38, height: 38))
|
||||
|
|
|
@ -381,7 +381,7 @@ extension VoiceMessageRecordingView {
|
|||
private func setUpViewHierarchy() {
|
||||
// Background & blur
|
||||
let backgroundView: UIView = UIView()
|
||||
backgroundView.themeBackgroundColor = .backgroundSecondary// .backgroundColor = isLightMode ? .white : .black
|
||||
backgroundView.themeBackgroundColor = .backgroundSecondary
|
||||
backgroundView.alpha = Values.lowOpacity
|
||||
addSubview(backgroundView)
|
||||
backgroundView.pin(to: self)
|
||||
|
|
|
@ -65,10 +65,13 @@ final class LinkPreviewView: UIView {
|
|||
private lazy var hStackView: UIStackView = UIStackView()
|
||||
|
||||
private lazy var cancelButton: UIButton = {
|
||||
// FIXME: This will have issues with theme transitions
|
||||
let result: UIButton = UIButton(type: .custom)
|
||||
result.setImage(UIImage(named: "X")?.withRenderingMode(.alwaysTemplate), for: UIControl.State.normal)
|
||||
result.tintColor = (isLightMode ? .black : .white)
|
||||
result.setImage(
|
||||
UIImage(named: "X")?
|
||||
.withRenderingMode(.alwaysTemplate),
|
||||
for: .normal
|
||||
)
|
||||
result.themeTintColor = .textPrimary
|
||||
|
||||
let cancelButtonSize = LinkPreviewView.cancelButtonSize
|
||||
result.set(.width, to: cancelButtonSize)
|
||||
|
|
|
@ -30,10 +30,10 @@ public class MessageCell: UITableViewCell {
|
|||
}
|
||||
|
||||
func setUpViewHierarchy() {
|
||||
backgroundColor = .clear
|
||||
themeBackgroundColor = .clear
|
||||
|
||||
let selectedBackgroundView = UIView()
|
||||
selectedBackgroundView.backgroundColor = .clear
|
||||
selectedBackgroundView.themeBackgroundColor = .clear
|
||||
self.selectedBackgroundView = selectedBackgroundView
|
||||
}
|
||||
|
||||
|
|
|
@ -1118,8 +1118,8 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
|
|||
)
|
||||
.forEach { range in
|
||||
let legacyRange: NSRange = NSRange(range, in: normalizedBody)
|
||||
attributedText.addAttribute(.backgroundColor, value: backgroundPrimaryColor, range: legacyRange)
|
||||
attributedText.addAttribute(.foregroundColor, value: textPrimaryColor, range: legacyRange)
|
||||
attributedText.addThemeAttribute(.background(backgroundPrimaryColor), range: legacyRange)
|
||||
attributedText.addThemeAttribute(.foreground(textPrimaryColor), range: legacyRange)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ final class ConversationTitleView: UIView {
|
|||
}
|
||||
guard !onlyNotifyForMentions else {
|
||||
let imageAttachment = NSTextAttachment()
|
||||
imageAttachment.image = UIImage(named: "NotifyMentions.png")?.asTintedImage(color: textPrimary)
|
||||
imageAttachment.image = UIImage(named: "NotifyMentions.png")?.withTint(textPrimary)
|
||||
imageAttachment.bounds = CGRect(
|
||||
x: 0,
|
||||
y: -2,
|
||||
|
|
|
@ -122,7 +122,7 @@ final class ReactionListSheet: BaseVC {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.backgroundColor = .clear
|
||||
view.themeBackgroundColor = .clear
|
||||
|
||||
let swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(close))
|
||||
swipeGestureRecognizer.direction = .down
|
||||
|
@ -133,6 +133,7 @@ final class ReactionListSheet: BaseVC {
|
|||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
reactionContainer.scrollToItem(
|
||||
at: IndexPath(item: lastSelectedReactionIndex, section: 0),
|
||||
at: .centeredHorizontally,
|
||||
|
@ -150,7 +151,7 @@ final class ReactionListSheet: BaseVC {
|
|||
view.addSubview(contentView)
|
||||
contentView.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing, UIView.VerticalEdge.bottom ], to: view)
|
||||
// Emoji collectionView height + seleted emoji detail height + 5 × user cell height + footer cell height + bottom safe area inset
|
||||
let contentViewHeight: CGFloat = 100 + 5 * 65 + 45 + UIApplication.shared.keyWindow!.safeAreaInsets.bottom
|
||||
let contentViewHeight: CGFloat = 100 + 5 * 65 + 45 + (UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0)
|
||||
contentView.set(.height, to: contentViewHeight)
|
||||
populateContentView()
|
||||
}
|
||||
|
@ -163,7 +164,7 @@ final class ReactionListSheet: BaseVC {
|
|||
|
||||
// Seperator
|
||||
let seperator = UIView()
|
||||
seperator.backgroundColor = Colors.border.withAlphaComponent(0.1)
|
||||
seperator.themeBackgroundColor = .borderSeparator
|
||||
seperator.set(.height, to: 0.5)
|
||||
contentView.addSubview(seperator)
|
||||
seperator.pin(.leading, to: .leading, of: contentView, withInset: Values.smallSpacing)
|
||||
|
@ -180,7 +181,7 @@ final class ReactionListSheet: BaseVC {
|
|||
// Line
|
||||
let line = UIView()
|
||||
line.set(.height, to: 0.5)
|
||||
line.backgroundColor = Colors.border.withAlphaComponent(0.5)
|
||||
line.themeBackgroundColor = .borderSeparator
|
||||
contentView.addSubview(line)
|
||||
line.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing ], to: contentView)
|
||||
line.pin(.top, to: .bottom, of: stackView, withInset: Values.smallSpacing)
|
||||
|
@ -541,12 +542,12 @@ extension ReactionListSheet {
|
|||
}
|
||||
|
||||
fileprivate final class FooterCell: UITableViewCell {
|
||||
|
||||
private lazy var label: UILabel = {
|
||||
let result = UILabel()
|
||||
result.textAlignment = .center
|
||||
let result: UILabel = UILabel()
|
||||
result.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
result.textColor = Colors.grey.withAlphaComponent(0.8)
|
||||
result.themeTextColor = .textSecondary
|
||||
result.textAlignment = .center
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
|
@ -554,17 +555,19 @@ extension ReactionListSheet {
|
|||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
private func setUpViewHierarchy() {
|
||||
// Background color
|
||||
backgroundColor = Colors.cellBackground
|
||||
themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
contentView.addSubview(label)
|
||||
label.pin(to: contentView)
|
||||
|
@ -572,9 +575,10 @@ extension ReactionListSheet {
|
|||
}
|
||||
|
||||
func update(moreReactorCount: Int, emoji: String) {
|
||||
label.text = (moreReactorCount == 1) ?
|
||||
label.text = (moreReactorCount == 1 ?
|
||||
String(format: "EMOJI_REACTS_MORE_REACTORS_ONE".localized(), "\(emoji)") :
|
||||
String(format: "EMOJI_REACTS_MORE_REACTORS_MUTIPLE".localized(), "\(moreReactorCount)" ,"\(emoji)")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
import GRDB
|
||||
import Curve25519Kit
|
||||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
final class NewDMVC: BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, OWSQRScannerDelegate {
|
||||
final class NewDMVC: BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, QRScannerDelegate {
|
||||
private let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
|
||||
private var pages: [UIViewController] = []
|
||||
private var targetVCIndex: Int?
|
||||
|
@ -38,7 +40,7 @@ final class NewDMVC: BaseVC, UIPageViewControllerDataSource, UIPageViewControlle
|
|||
|
||||
private lazy var scanQRCodePlaceholderVC: ScanQRCodePlaceholderVC = {
|
||||
let result = ScanQRCodePlaceholderVC()
|
||||
result.NewDMVC = self
|
||||
result.newDMVC = self
|
||||
|
||||
return result
|
||||
}()
|
||||
|
@ -142,7 +144,7 @@ final class NewDMVC: BaseVC, UIPageViewControllerDataSource, UIPageViewControlle
|
|||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func controller(_ controller: OWSQRCodeScanningViewController, didDetectQRCodeWith string: String) {
|
||||
func controller(_ controller: QRCodeScanningViewController, didDetectQRCodeWith string: String) {
|
||||
let hexEncodedPublicKey = string
|
||||
startNewDMIfPossible(with: hexEncodedPublicKey)
|
||||
}
|
||||
|
@ -450,7 +452,7 @@ private final class EnterPublicKeyVC: UIViewController {
|
|||
// MARK: - ScanQRCodePlaceholderVC
|
||||
|
||||
private final class ScanQRCodePlaceholderVC: UIViewController {
|
||||
weak var NewDMVC: NewDMVC!
|
||||
weak var newDMVC: NewDMVC!
|
||||
|
||||
override func viewDidLoad() {
|
||||
// Remove background color
|
||||
|
@ -493,12 +495,8 @@ private final class ScanQRCodePlaceholderVC: UIViewController {
|
|||
}
|
||||
|
||||
@objc private func requestCameraAccess() {
|
||||
ows_ask(forCameraPermissions: { [weak self] hasCameraAccess in
|
||||
if hasCameraAccess {
|
||||
self?.NewDMVC.handleCameraAccessGranted()
|
||||
} else {
|
||||
// Do nothing
|
||||
}
|
||||
})
|
||||
Permissions.requestLibraryPermissionIfNeeded { [weak self] in
|
||||
self?.newDMVC.handleCameraAccessGranted()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ class MessageRequestsViewController: BaseVC, UITableViewDelegate, UITableViewDat
|
|||
private lazy var tableView: UITableView = {
|
||||
let result: UITableView = UITableView()
|
||||
result.translatesAutoresizingMaskIntoConstraints = false
|
||||
result.backgroundColor = .clear
|
||||
result.themeBackgroundColor = .clear
|
||||
result.separatorStyle = .none
|
||||
result.register(view: FullConversationCell.self)
|
||||
result.dataSource = self
|
||||
|
@ -277,7 +277,7 @@ class MessageRequestsViewController: BaseVC, UITableViewDelegate, UITableViewDat
|
|||
switch section.model {
|
||||
case .loadMore:
|
||||
let loadingIndicator: UIActivityIndicatorView = UIActivityIndicatorView(style: .medium)
|
||||
loadingIndicator.tintColor = Colors.text
|
||||
loadingIndicator.themeTintColor = .textPrimary
|
||||
loadingIndicator.alpha = 0.5
|
||||
loadingIndicator.startAnimating()
|
||||
|
||||
|
|
|
@ -12,22 +12,27 @@ public class AllMediaViewController: UIViewController, UIPageViewControllerDataS
|
|||
private var pages: [UIViewController] = []
|
||||
private var targetVCIndex: Int?
|
||||
|
||||
// MARK: Components
|
||||
// MARK: - Components
|
||||
|
||||
private lazy var tabBar: TabBar = {
|
||||
let tabs = [
|
||||
TabBar.Tab(title: MediaStrings.media) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
|
||||
self.updateSelectButton(updatedData: self.mediaTitleViewController.viewModel.galleryData, inBatchSelectMode: self.mediaTitleViewController.isInBatchSelectMode)
|
||||
},
|
||||
TabBar.Tab(title: MediaStrings.document) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
|
||||
self.endSelectMode()
|
||||
self.navigationItem.rightBarButtonItem = nil
|
||||
}
|
||||
]
|
||||
return TabBar(tabs: tabs)
|
||||
let result: TabBar = TabBar(
|
||||
tabs: [
|
||||
TabBar.Tab(title: MediaStrings.media) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
|
||||
self.updateSelectButton(updatedData: self.mediaTitleViewController.viewModel.galleryData, inBatchSelectMode: self.mediaTitleViewController.isInBatchSelectMode)
|
||||
},
|
||||
TabBar.Tab(title: MediaStrings.document) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
|
||||
self.endSelectMode()
|
||||
self.navigationItem.rightBarButtonItem = nil
|
||||
}
|
||||
]
|
||||
)
|
||||
result.themeBackgroundColor = .backgroundPrimary
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private var mediaTitleViewController: MediaTileViewController
|
||||
|
@ -54,11 +59,11 @@ public class AllMediaViewController: UIViewController, UIPageViewControllerDataS
|
|||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.themeBackgroundColor = .backgroundPrimary
|
||||
view.themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
// Add a custom back button if this is the only view controller
|
||||
if self.navigationController?.viewControllers.first == self {
|
||||
let backButton = OWSViewController.createOWSBackButton(withTarget: self, selector: #selector(didPressDismissButton))
|
||||
let backButton = UIViewController.createOWSBackButton(target: self, selector: #selector(didPressDismissButton))
|
||||
self.navigationItem.leftBarButtonItem = backButton
|
||||
}
|
||||
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
@objc public class AudioProgressView: UIView {
|
||||
|
||||
@objc public override var bounds: CGRect {
|
||||
didSet {
|
||||
if oldValue != bounds {
|
||||
updateSubviews()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc public override var frame: CGRect {
|
||||
didSet {
|
||||
if oldValue != frame {
|
||||
updateSubviews()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc public var horizontalBarColor = UIColor.black {
|
||||
didSet {
|
||||
updateContent()
|
||||
}
|
||||
}
|
||||
|
||||
@objc public var progressColor = UIColor.blue {
|
||||
didSet {
|
||||
updateContent()
|
||||
}
|
||||
}
|
||||
|
||||
private let horizontalBarLayer: CAShapeLayer
|
||||
private let progressLayer: CAShapeLayer
|
||||
|
||||
@objc public var progress: CGFloat = 0 {
|
||||
didSet {
|
||||
if oldValue != progress {
|
||||
updateContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable, message:"use other constructor instead.")
|
||||
@objc public required init?(coder aDecoder: NSCoder) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
public required init() {
|
||||
self.horizontalBarLayer = CAShapeLayer()
|
||||
self.progressLayer = CAShapeLayer()
|
||||
|
||||
super.init(frame: CGRect.zero)
|
||||
|
||||
self.layer.addSublayer(self.horizontalBarLayer)
|
||||
self.layer.addSublayer(self.progressLayer)
|
||||
}
|
||||
|
||||
internal func updateSubviews() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
self.horizontalBarLayer.frame = self.bounds
|
||||
self.progressLayer.frame = self.bounds
|
||||
|
||||
updateContent()
|
||||
}
|
||||
|
||||
internal func updateContent() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
// Prevent the shape layer from animating changes.
|
||||
CATransaction.begin()
|
||||
CATransaction.setDisableActions(true)
|
||||
|
||||
let horizontalBarPath = UIBezierPath()
|
||||
let horizontalBarHeightFraction = CGFloat(0.25)
|
||||
let horizontalBarHeight = bounds.size.height * horizontalBarHeightFraction
|
||||
horizontalBarPath.append(UIBezierPath(rect: CGRect(x: 0, y: (bounds.size.height - horizontalBarHeight) * 0.5, width: bounds.size.width, height: horizontalBarHeight)))
|
||||
horizontalBarLayer.path = horizontalBarPath.cgPath
|
||||
horizontalBarLayer.fillColor = horizontalBarColor.cgColor
|
||||
|
||||
let progressHeight = bounds.self.height
|
||||
let progressWidth = progressHeight * 0.15
|
||||
let progressX = (bounds.self.width - progressWidth) * max(0.0, min(1.0, progress))
|
||||
let progressBounds = CGRect(x: progressX, y: 0, width: progressWidth, height: progressHeight)
|
||||
let progressCornerRadius = progressWidth * 0.5
|
||||
let progressPath = UIBezierPath()
|
||||
progressPath.append(UIBezierPath(roundedRect: progressBounds, cornerRadius: progressCornerRadius))
|
||||
progressLayer.path = progressPath.cgPath
|
||||
progressLayer.fillColor = progressColor.cgColor
|
||||
|
||||
CATransaction.commit()
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import Foundation
|
||||
import MediaPlayer
|
||||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
// This kind of view is tricky. I've tried to organize things in the
|
||||
|
@ -145,13 +146,37 @@ import SignalUtilitiesKit
|
|||
// MARK: - Create Views
|
||||
|
||||
private func createViews() {
|
||||
|
||||
view.backgroundColor = .black
|
||||
view.themeBackgroundColor = .backgroundPrimary
|
||||
|
||||
let contentView = UIView()
|
||||
contentView.backgroundColor = .black
|
||||
contentView.themeBackgroundColor = .backgroundPrimary
|
||||
self.view.addSubview(contentView)
|
||||
contentView.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
let titleLabel: UILabel = UILabel()
|
||||
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
||||
titleLabel.text = "CROP_SCALE_IMAGE_VIEW_TITLE".localized()
|
||||
titleLabel.themeTextColor = .textPrimary
|
||||
titleLabel.textAlignment = .center
|
||||
contentView.addSubview(titleLabel)
|
||||
titleLabel.autoPinWidthToSuperview()
|
||||
|
||||
let titleLabelMargin = ScaleFromIPhone5(16)
|
||||
titleLabel.autoPinEdge(toSuperviewSafeArea: .top, withInset: titleLabelMargin)
|
||||
|
||||
let buttonRow: UIView = createButtonRow()
|
||||
contentView.addSubview(buttonRow)
|
||||
buttonRow.pin(.leading, to: .leading, of: contentView)
|
||||
buttonRow.pin(.trailing, to: .trailing, of: contentView)
|
||||
buttonRow.pin(.bottom, to: .bottom, of: contentView)
|
||||
buttonRow.set(
|
||||
.height,
|
||||
to: (
|
||||
ScaleFromIPhone5To7Plus(35, 45) +
|
||||
Values.mediumSpacing +
|
||||
(UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? Values.mediumSpacing)
|
||||
)
|
||||
)
|
||||
|
||||
let imageView = OWSLayerView(frame: CGRect.zero, layoutCallback: { [weak self] _ in
|
||||
guard let strongSelf = self else { return }
|
||||
|
@ -160,7 +185,10 @@ import SignalUtilitiesKit
|
|||
imageView.clipsToBounds = true
|
||||
self.imageView = imageView
|
||||
contentView.addSubview(imageView)
|
||||
imageView.autoPinEdgesToSuperviewEdges()
|
||||
imageView.pin(.top, to: .top, of: contentView, withInset: (Values.massiveSpacing + Values.smallSpacing))
|
||||
imageView.pin(.leading, to: .leading, of: contentView)
|
||||
imageView.pin(.trailing, to: .trailing, of: contentView)
|
||||
imageView.pin(.bottom, to: .top, of: buttonRow)
|
||||
|
||||
let imageLayer = CALayer()
|
||||
self.imageLayer = imageLayer
|
||||
|
@ -185,23 +213,13 @@ import SignalUtilitiesKit
|
|||
|
||||
layer.path = path.cgPath
|
||||
layer.fillRule = .evenOdd
|
||||
layer.fillColor = UIColor.black.cgColor
|
||||
layer.themeFillColor = .black
|
||||
layer.opacity = 0.75
|
||||
}
|
||||
maskingView.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.textColor = .white
|
||||
titleLabel.textAlignment = .center
|
||||
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
||||
titleLabel.text = NSLocalizedString("CROP_SCALE_IMAGE_VIEW_TITLE",
|
||||
comment: "Title for the 'crop/scale image' dialog.")
|
||||
contentView.addSubview(titleLabel)
|
||||
titleLabel.autoPinWidthToSuperview()
|
||||
let titleLabelMargin = ScaleFromIPhone5(16)
|
||||
titleLabel.autoPinEdge(toSuperviewSafeArea: .top, withInset: titleLabelMargin)
|
||||
|
||||
createButtonRow(contentView: contentView)
|
||||
maskingView.pin(.top, to: .top, of: contentView, withInset: (Values.massiveSpacing + Values.smallSpacing))
|
||||
maskingView.pin(.leading, to: .leading, of: contentView)
|
||||
maskingView.pin(.trailing, to: .trailing, of: contentView)
|
||||
maskingView.pin(.bottom, to: .top, of: buttonRow)
|
||||
|
||||
contentView.isUserInteractionEnabled = true
|
||||
contentView.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(sender:))))
|
||||
|
@ -427,45 +445,35 @@ import SignalUtilitiesKit
|
|||
updateImageLayout()
|
||||
}
|
||||
|
||||
private func createButtonRow(contentView: UIView) {
|
||||
let buttonTopMargin = ScaleFromIPhone5To7Plus(30, 40)
|
||||
let buttonBottomMargin = ScaleFromIPhone5To7Plus(25, 40)
|
||||
private func createButtonRow() -> UIView {
|
||||
let result: UIStackView = UIStackView()
|
||||
result.axis = .horizontal
|
||||
result.distribution = .fillEqually
|
||||
result.alignment = .fill
|
||||
|
||||
let buttonRow = UIView()
|
||||
self.view.addSubview(buttonRow)
|
||||
buttonRow.autoPinWidthToSuperview()
|
||||
buttonRow.autoPinEdge(toSuperviewEdge: .bottom, withInset: buttonBottomMargin)
|
||||
buttonRow.autoPinEdge(.top, to: .bottom, of: contentView, withOffset: buttonTopMargin)
|
||||
let cancelButton = createButton(title: CommonStrings.cancelButton, action: #selector(cancelPressed))
|
||||
result.addArrangedSubview(cancelButton)
|
||||
|
||||
let cancelButton = createButton(title: CommonStrings.cancelButton,
|
||||
action: #selector(cancelPressed))
|
||||
cancelButton.titleLabel!.font = .systemFont(ofSize: 18) // Match iOS UI
|
||||
buttonRow.addSubview(cancelButton)
|
||||
cancelButton.autoPinEdge(toSuperviewEdge: .top)
|
||||
cancelButton.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
cancelButton.autoPinEdge(toSuperviewEdge: .left)
|
||||
|
||||
let doneButton = createButton(title: CommonStrings.doneButton,
|
||||
action: #selector(donePressed))
|
||||
doneButton.titleLabel!.font = .systemFont(ofSize: 18) // Match iOS UI
|
||||
buttonRow.addSubview(doneButton)
|
||||
doneButton.autoPinEdge(toSuperviewEdge: .top)
|
||||
doneButton.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
doneButton.autoPinEdge(toSuperviewEdge: .right)
|
||||
let doneButton = createButton(title: CommonStrings.doneButton, action: #selector(donePressed))
|
||||
result.addArrangedSubview(doneButton)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private func createButton(title: String, action: Selector) -> UIButton {
|
||||
let buttonFont = UIFont.ows_mediumFont(withSize: ScaleFromIPhone5To7Plus(18, 22))
|
||||
let buttonWidth = ScaleFromIPhone5To7Plus(110, 140)
|
||||
let buttonHeight = ScaleFromIPhone5To7Plus(35, 45)
|
||||
|
||||
let button = UIButton()
|
||||
let button: UIButton = UIButton()
|
||||
button.titleLabel?.font = .systemFont(ofSize: 18)
|
||||
button.setTitle(title, for: .normal)
|
||||
button.setTitleColor(UIColor.white, for: .normal)
|
||||
button.titleLabel!.font = buttonFont
|
||||
button.setThemeTitleColor(.textPrimary, for: .normal)
|
||||
button.setThemeBackgroundColor(.backgroundSecondary, for: .highlighted)
|
||||
button.contentEdgeInsets = UIEdgeInsets(
|
||||
top: Values.mediumSpacing,
|
||||
leading: 0,
|
||||
bottom: (UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? Values.mediumSpacing),
|
||||
trailing: 0
|
||||
)
|
||||
button.addTarget(self, action: action, for: .touchUpInside)
|
||||
button.autoSetDimension(.width, toSize: buttonWidth)
|
||||
button.autoSetDimension(.height, toSize: buttonHeight)
|
||||
|
||||
return button
|
||||
}
|
||||
|
||||
|
|
|
@ -49,8 +49,8 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate,
|
|||
}
|
||||
|
||||
lazy var tableView: UITableView = {
|
||||
let result = UITableView(frame: .zero, style: .grouped)
|
||||
result.backgroundColor = Colors.navigationBarBackground
|
||||
let result: UITableView = UITableView()
|
||||
result.themeBackgroundColor = .backgroundSecondary
|
||||
result.separatorStyle = .none
|
||||
result.showsVerticalScrollIndicator = false
|
||||
result.register(view: DocumentCell.self)
|
||||
|
@ -59,6 +59,10 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate,
|
|||
// Feels a bit weird to have content smashed all the way to the bottom edge.
|
||||
result.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0)
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
result.sectionHeaderTopPadding = 0
|
||||
}
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
|
@ -69,7 +73,7 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate,
|
|||
|
||||
// Add a custom back button if this is the only view controller
|
||||
if self.navigationController?.viewControllers.first == self {
|
||||
let backButton = OWSViewController.createOWSBackButton(withTarget: self, selector: #selector(didPressDismissButton))
|
||||
let backButton = UIViewController.createOWSBackButton(target: self, selector: #selector(didPressDismissButton))
|
||||
self.navigationItem.leftBarButtonItem = backButton
|
||||
}
|
||||
|
||||
|
@ -363,7 +367,8 @@ class DocumentCell: UITableViewCell {
|
|||
private let iconImageView: UIImageView = {
|
||||
let result: UIImageView = UIImageView(image: #imageLiteral(resourceName: "File").withRenderingMode(.alwaysTemplate))
|
||||
result.translatesAutoresizingMaskIntoConstraints = false
|
||||
result.tintColor = Colors.text
|
||||
result.themeTintColor = .textPrimary
|
||||
result.contentMode = .scaleAspectFit
|
||||
|
||||
return result
|
||||
}()
|
||||
|
@ -374,7 +379,20 @@ class DocumentCell: UITableViewCell {
|
|||
result.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
result.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
result.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
||||
result.textColor = Colors.text
|
||||
result.themeTextColor = .textPrimary
|
||||
result.lineBreakMode = .byTruncatingTail
|
||||
result.numberOfLines = 2
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private let timeLabel: UILabel = {
|
||||
let result: UILabel = UILabel()
|
||||
result.translatesAutoresizingMaskIntoConstraints = false
|
||||
result.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
result.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||
result.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
result.themeTextColor = .textSecondary
|
||||
result.lineBreakMode = .byTruncatingTail
|
||||
|
||||
return result
|
||||
|
@ -386,20 +404,26 @@ class DocumentCell: UITableViewCell {
|
|||
result.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
result.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
result.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
result.textColor = Colors.text
|
||||
result.themeTextColor = .textSecondary
|
||||
result.lineBreakMode = .byTruncatingTail
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private func setUpViewHierarchy() {
|
||||
backgroundColor = Colors.cellBackground
|
||||
selectedBackgroundView = UIView()
|
||||
selectedBackgroundView?.backgroundColor = Colors.cellSelected
|
||||
themeBackgroundColor = .clear
|
||||
|
||||
backgroundView = UIView()
|
||||
backgroundView?.themeBackgroundColor = .conversationButton_background
|
||||
backgroundView?.layer.cornerRadius = 5
|
||||
|
||||
selectedBackgroundView = UIView()
|
||||
selectedBackgroundView?.themeBackgroundColor = .conversationButton_highlight
|
||||
selectedBackgroundView?.layer.cornerRadius = 5
|
||||
|
||||
contentView.addSubview(iconImageView)
|
||||
contentView.addSubview(titleLabel)
|
||||
contentView.addSubview(timeLabel)
|
||||
contentView.addSubview(detailLabel)
|
||||
}
|
||||
|
||||
|
@ -407,57 +431,110 @@ class DocumentCell: UITableViewCell {
|
|||
|
||||
private func setupLayout() {
|
||||
NSLayoutConstraint.activate([
|
||||
contentView.heightAnchor.constraint(equalToConstant: 68),
|
||||
|
||||
iconImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: Values.mediumSpacing),
|
||||
iconImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
||||
iconImageView.widthAnchor.constraint(equalToConstant: Self.iconImageViewSize.width),
|
||||
iconImageView.heightAnchor.constraint(equalToConstant: Self.iconImageViewSize.height),
|
||||
iconImageView.topAnchor.constraint(
|
||||
greaterThanOrEqualTo: contentView.topAnchor,
|
||||
constant: (Values.verySmallSpacing + Values.verySmallSpacing)
|
||||
),
|
||||
iconImageView.leftAnchor.constraint(
|
||||
equalTo: contentView.leftAnchor,
|
||||
constant: (Values.largeSpacing + Values.mediumSpacing)
|
||||
),
|
||||
iconImageView.bottomAnchor.constraint(
|
||||
lessThanOrEqualTo: contentView.bottomAnchor,
|
||||
constant: -(Values.verySmallSpacing + Values.verySmallSpacing)
|
||||
),
|
||||
|
||||
titleLabel.topAnchor.constraint(
|
||||
equalTo: contentView.topAnchor,
|
||||
constant: (Values.verySmallSpacing + Values.verySmallSpacing)
|
||||
),
|
||||
titleLabel.leftAnchor.constraint(equalTo: iconImageView.rightAnchor, constant: Values.mediumSpacing),
|
||||
titleLabel.rightAnchor.constraint(lessThanOrEqualTo: contentView.rightAnchor, constant: -Values.mediumSpacing),
|
||||
titleLabel.topAnchor.constraint(equalTo: iconImageView.topAnchor),
|
||||
titleLabel.rightAnchor.constraint(
|
||||
lessThanOrEqualTo: timeLabel.leftAnchor,
|
||||
constant: -Values.mediumSpacing
|
||||
),
|
||||
|
||||
timeLabel.topAnchor.constraint(equalTo: iconImageView.topAnchor),
|
||||
timeLabel.rightAnchor.constraint(
|
||||
equalTo: contentView.rightAnchor,
|
||||
constant: -(Values.mediumSpacing + Values.largeSpacing)
|
||||
),
|
||||
|
||||
detailLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: Values.smallSpacing),
|
||||
detailLabel.leftAnchor.constraint(equalTo: iconImageView.rightAnchor, constant: Values.mediumSpacing),
|
||||
detailLabel.rightAnchor.constraint(lessThanOrEqualTo: contentView.rightAnchor, constant: -Values.mediumSpacing),
|
||||
detailLabel.bottomAnchor.constraint(equalTo: iconImageView.bottomAnchor),
|
||||
detailLabel.rightAnchor.constraint(
|
||||
lessThanOrEqualTo: contentView.rightAnchor,
|
||||
constant: -(Values.verySmallSpacing + Values.largeSpacing)
|
||||
),
|
||||
detailLabel.bottomAnchor.constraint(
|
||||
lessThanOrEqualTo: contentView.bottomAnchor,
|
||||
constant: -(Values.verySmallSpacing + Values.smallSpacing)
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
backgroundView?.frame = CGRect(
|
||||
x: Values.largeSpacing,
|
||||
y: Values.verySmallSpacing,
|
||||
width: (contentView.bounds.width - (Values.largeSpacing * 2)),
|
||||
height: (contentView.bounds.height - (Values.verySmallSpacing * 2))
|
||||
)
|
||||
selectedBackgroundView?.frame = (backgroundView?.frame ?? .zero)
|
||||
}
|
||||
|
||||
// MARK: - Content
|
||||
|
||||
func update(with item: MediaGalleryViewModel.Item) {
|
||||
let attachment = item.attachment
|
||||
titleLabel.text = attachment.sourceFilename ?? "File"
|
||||
titleLabel.text = (attachment.sourceFilename ?? "File")
|
||||
detailLabel.text = "\(OWSFormat.formatFileSize(UInt(attachment.byteCount)))"
|
||||
timeLabel.text = Date(
|
||||
timeIntervalSince1970: TimeInterval(item.interactionTimestampMs / 1000)
|
||||
).formattedForDisplay
|
||||
}
|
||||
}
|
||||
|
||||
class DocumentSectionHeaderView: UIView {
|
||||
// HACK: scrollbar incorrectly appears *behind* section headers
|
||||
// in collection view on iOS11 =(
|
||||
private class AlwaysOnTopLayer: CALayer {
|
||||
override var zPosition: CGFloat {
|
||||
get { return 0 }
|
||||
set {}
|
||||
}
|
||||
}
|
||||
|
||||
let label: UILabel
|
||||
|
||||
override class var layerClass: AnyClass {
|
||||
get {
|
||||
// HACK: scrollbar incorrectly appears *behind* section headers
|
||||
// in collection view on iOS11 =(
|
||||
return AlwaysOnTopLayer.self
|
||||
}
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
label = UILabel()
|
||||
label.textColor = Colors.text
|
||||
|
||||
let blurEffect = UIBlurEffect(style: .dark)
|
||||
let blurEffectView = UIVisualEffectView(effect: blurEffect)
|
||||
|
||||
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
label.themeTextColor = .textPrimary
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.backgroundColor = isLightMode ? Colors.cellBackground : UIColor.ows_black.withAlphaComponent(OWSNavigationBar.backgroundBlurMutingFactor)
|
||||
self.themeBackgroundColor = .clear
|
||||
|
||||
let backgroundView: UIView = UIView()
|
||||
backgroundView.themeBackgroundColor = .backgroundSecondary
|
||||
addSubview(backgroundView)
|
||||
backgroundView.pin(to: self)
|
||||
|
||||
self.addSubview(blurEffectView)
|
||||
self.addSubview(label)
|
||||
|
||||
blurEffectView.autoPinEdgesToSuperviewEdges()
|
||||
blurEffectView.isHidden = isLightMode
|
||||
label.autoPinEdge(toSuperviewMargin: .trailing)
|
||||
label.autoPinEdge(toSuperviewMargin: .leading)
|
||||
label.autoVCenterInSuperview()
|
||||
label.pin(.leading, to: .leading, of: self, withInset: Values.largeSpacing)
|
||||
label.pin(.trailing, to: .trailing, of: self, withInset: -Values.largeSpacing)
|
||||
label.center(.vertical, in: self)
|
||||
}
|
||||
|
||||
@available(*, unavailable, message: "Unimplemented")
|
||||
|
@ -479,7 +556,7 @@ class DocumentStaticHeaderView: UIView {
|
|||
|
||||
addSubview(label)
|
||||
|
||||
label.textColor = Colors.text
|
||||
label.themeTextColor = .textPrimary
|
||||
label.textAlignment = .center
|
||||
label.numberOfLines = 0
|
||||
label.autoPinEdgesToSuperviewMargins(with: UIEdgeInsets(top: 0, leading: Values.largeSpacing, bottom: 0, trailing: Values.largeSpacing))
|
||||
|
|
|
@ -218,7 +218,7 @@ class GifPickerCell: UICollectionViewCell {
|
|||
return
|
||||
}
|
||||
imageView.image = image
|
||||
self.backgroundColor = nil
|
||||
self.themeBackgroundColor = nil
|
||||
|
||||
if self.isCellSelected {
|
||||
let activityIndicator = UIActivityIndicatorView(style: .gray)
|
||||
|
@ -229,11 +229,12 @@ class GifPickerCell: UICollectionViewCell {
|
|||
|
||||
// Render activityIndicator on a white tile to ensure it's visible on
|
||||
// when overlayed on a variety of potential gifs.
|
||||
activityIndicator.backgroundColor = UIColor.white.withAlphaComponent(0.3)
|
||||
activityIndicator.themeBackgroundColor = .white
|
||||
activityIndicator.alpha = 0.3
|
||||
activityIndicator.autoSetDimension(.width, toSize: 30)
|
||||
activityIndicator.autoSetDimension(.height, toSize: 30)
|
||||
activityIndicator.themeShadowColor = .black
|
||||
activityIndicator.layer.cornerRadius = 3
|
||||
activityIndicator.layer.shadowColor = UIColor.black.cgColor
|
||||
activityIndicator.layer.shadowOffset = CGSize(width: 1, height: 1)
|
||||
activityIndicator.layer.shadowOpacity = 0.7
|
||||
activityIndicator.layer.shadowRadius = 1.0
|
||||
|
@ -270,9 +271,7 @@ class GifPickerCell: UICollectionViewCell {
|
|||
|
||||
private func clearViewState() {
|
||||
imageView?.image = nil
|
||||
self.backgroundColor = (isDarkMode
|
||||
? UIColor(white: 0.25, alpha: 1.0)
|
||||
: UIColor(white: 0.95, alpha: 1.0))
|
||||
self.themeBackgroundColor = .backgroundSecondary
|
||||
}
|
||||
|
||||
private func pickBestAsset() -> ProxiedContentAsset? {
|
||||
|
|
|
@ -99,28 +99,35 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel,
|
||||
target: self,
|
||||
action: #selector(donePressed))
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(
|
||||
barButtonSystemItem: .cancel,
|
||||
target: self,
|
||||
action: #selector(donePressed)
|
||||
)
|
||||
|
||||
// Loki: Customize title
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.text = "accessibility_gif_button".localized().uppercased()
|
||||
titleLabel.textColor = Colors.text
|
||||
let titleLabel: UILabel = UILabel()
|
||||
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
||||
titleLabel.text = "accessibility_gif_button".localized().uppercased()
|
||||
titleLabel.themeTextColor = .textPrimary
|
||||
navigationItem.titleView = titleLabel
|
||||
|
||||
createViews()
|
||||
|
||||
reachability = Reachability.forInternetConnection()
|
||||
NotificationCenter.default.addObserver(self,
|
||||
selector: #selector(reachabilityChanged),
|
||||
name: NSNotification.Name.reachabilityChanged,
|
||||
object: nil)
|
||||
NotificationCenter.default.addObserver(self,
|
||||
selector: #selector(didBecomeActive),
|
||||
name: NSNotification.Name.OWSApplicationDidBecomeActive,
|
||||
object: nil)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(reachabilityChanged),
|
||||
name: .reachabilityChanged,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(didBecomeActive),
|
||||
name: .OWSApplicationDidBecomeActive,
|
||||
object: nil
|
||||
)
|
||||
|
||||
loadTrending()
|
||||
}
|
||||
|
||||
|
@ -140,14 +147,8 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
// MARK: Views
|
||||
|
||||
private func createViews() {
|
||||
|
||||
let backgroundColor = Colors.navigationBarBackground
|
||||
self.view.backgroundColor = backgroundColor
|
||||
|
||||
// Block UIKit from adjust insets of collection view which screws up
|
||||
// min/max scroll positions.
|
||||
self.automaticallyAdjustsScrollViewInsets = false
|
||||
|
||||
self.view.themeBackgroundColor = .backgroundPrimary
|
||||
|
||||
// Search
|
||||
searchBar.delegate = self
|
||||
|
||||
|
@ -157,7 +158,7 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
|
||||
self.collectionView.delegate = self
|
||||
self.collectionView.dataSource = self
|
||||
self.collectionView.backgroundColor = backgroundColor
|
||||
self.collectionView.themeBackgroundColor = .backgroundPrimary
|
||||
self.collectionView.register(GifPickerCell.self, forCellWithReuseIdentifier: kCellReuseIdentifier)
|
||||
// Inserted below searchbar because we later occlude the collectionview
|
||||
// by inserting a masking layer between the search bar and collectionview
|
||||
|
@ -165,10 +166,14 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
self.collectionView.autoPinEdge(toSuperviewSafeArea: .leading)
|
||||
self.collectionView.autoPinEdge(toSuperviewSafeArea: .trailing)
|
||||
self.collectionView.autoPinEdge(.top, to: .bottom, of: searchBar)
|
||||
|
||||
// Block UIKit from adjust insets of collection view which screws up
|
||||
// min/max scroll positions
|
||||
self.collectionView.contentInsetAdjustmentBehavior = .never
|
||||
|
||||
// for iPhoneX devices, extends the black background to the bottom edge of the view.
|
||||
let bottomBannerContainer = UIView()
|
||||
bottomBannerContainer.backgroundColor = isLightMode ? UIColor.black : Colors.navigationBarBackground
|
||||
bottomBannerContainer.themeBackgroundColor = .backgroundPrimary
|
||||
self.view.addSubview(bottomBannerContainer)
|
||||
bottomBannerContainer.autoPinWidthToSuperview()
|
||||
bottomBannerContainer.autoPinEdge(.top, to: .bottom, of: self.collectionView)
|
||||
|
@ -188,15 +193,13 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
logoImageView.autoPinHeightToSuperview(withMargin: 3)
|
||||
logoImageView.autoHCenterInSuperview()
|
||||
|
||||
let noResultsView = createErrorLabel(text: NSLocalizedString("GIF_VIEW_SEARCH_NO_RESULTS",
|
||||
comment: "Indicates that the user's search had no results."))
|
||||
let noResultsView = createErrorLabel(text: "GIF_VIEW_SEARCH_NO_RESULTS".localized())
|
||||
self.noResultsView = noResultsView
|
||||
self.view.addSubview(noResultsView)
|
||||
noResultsView.autoPinWidthToSuperview(withMargin: 20)
|
||||
noResultsView.autoAlignAxis(.horizontal, toSameAxisOf: self.collectionView)
|
||||
|
||||
let searchErrorView = createErrorLabel(text: NSLocalizedString("GIF_VIEW_SEARCH_ERROR",
|
||||
comment: "Indicates that an error occurred while searching."))
|
||||
let searchErrorView = createErrorLabel(text: "GIF_VIEW_SEARCH_ERROR".localized())
|
||||
self.searchErrorView = searchErrorView
|
||||
self.view.addSubview(searchErrorView)
|
||||
searchErrorView.autoPinWidthToSuperview(withMargin: 20)
|
||||
|
@ -205,29 +208,24 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
searchErrorView.isUserInteractionEnabled = true
|
||||
searchErrorView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(retryTapped)))
|
||||
|
||||
let activityIndicator = UIActivityIndicatorView(style: .whiteLarge)
|
||||
let activityIndicator = UIActivityIndicatorView(style: .large)
|
||||
self.activityIndicator = activityIndicator
|
||||
self.view.addSubview(activityIndicator)
|
||||
activityIndicator.autoHCenterInSuperview()
|
||||
activityIndicator.autoAlignAxis(.horizontal, toSameAxisOf: self.collectionView)
|
||||
|
||||
let navigationBar = navigationController!.navigationBar
|
||||
navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
|
||||
navigationBar.shadowImage = UIImage()
|
||||
navigationBar.isTranslucent = false
|
||||
navigationBar.barTintColor = Colors.navigationBarBackground
|
||||
|
||||
self.updateContents()
|
||||
}
|
||||
|
||||
private func createErrorLabel(text: String) -> UILabel {
|
||||
let label = UILabel()
|
||||
let label: UILabel = UILabel()
|
||||
label.font = .ows_mediumFont(withSize: 20)
|
||||
label.text = text
|
||||
label.textColor = Colors.text
|
||||
label.font = UIFont.ows_mediumFont(withSize: 20)
|
||||
label.themeTextColor = .textPrimary
|
||||
label.textAlignment = .center
|
||||
label.numberOfLines = 0
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
label.numberOfLines = 0
|
||||
|
||||
return label
|
||||
}
|
||||
|
||||
|
@ -246,39 +244,43 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
}
|
||||
|
||||
switch viewMode {
|
||||
case .idle:
|
||||
self.collectionView.isHidden = true
|
||||
noResultsView.isHidden = true
|
||||
searchErrorView.isHidden = true
|
||||
activityIndicator.isHidden = true
|
||||
activityIndicator.stopAnimating()
|
||||
case .searching:
|
||||
self.collectionView.isHidden = true
|
||||
noResultsView.isHidden = true
|
||||
searchErrorView.isHidden = true
|
||||
activityIndicator.isHidden = false
|
||||
activityIndicator.startAnimating()
|
||||
case .results:
|
||||
self.collectionView.isHidden = false
|
||||
noResultsView.isHidden = true
|
||||
searchErrorView.isHidden = true
|
||||
activityIndicator.isHidden = true
|
||||
activityIndicator.stopAnimating()
|
||||
case .idle:
|
||||
self.collectionView.isHidden = true
|
||||
noResultsView.isHidden = true
|
||||
searchErrorView.isHidden = true
|
||||
activityIndicator.isHidden = true
|
||||
activityIndicator.stopAnimating()
|
||||
|
||||
case .searching:
|
||||
self.collectionView.isHidden = true
|
||||
noResultsView.isHidden = true
|
||||
searchErrorView.isHidden = true
|
||||
activityIndicator.isHidden = false
|
||||
activityIndicator.startAnimating()
|
||||
|
||||
case .results:
|
||||
self.collectionView.isHidden = false
|
||||
noResultsView.isHidden = true
|
||||
searchErrorView.isHidden = true
|
||||
activityIndicator.isHidden = true
|
||||
activityIndicator.stopAnimating()
|
||||
|
||||
self.collectionView.collectionViewLayout.invalidateLayout()
|
||||
self.collectionView.reloadData()
|
||||
case .noResults:
|
||||
self.collectionView.isHidden = true
|
||||
noResultsView.isHidden = false
|
||||
searchErrorView.isHidden = true
|
||||
activityIndicator.isHidden = true
|
||||
activityIndicator.stopAnimating()
|
||||
case .error:
|
||||
self.collectionView.isHidden = true
|
||||
noResultsView.isHidden = true
|
||||
searchErrorView.isHidden = false
|
||||
activityIndicator.isHidden = true
|
||||
activityIndicator.stopAnimating()
|
||||
self.collectionView.collectionViewLayout.invalidateLayout()
|
||||
self.collectionView.reloadData()
|
||||
|
||||
case .noResults:
|
||||
self.collectionView.isHidden = true
|
||||
noResultsView.isHidden = false
|
||||
searchErrorView.isHidden = true
|
||||
activityIndicator.isHidden = true
|
||||
activityIndicator.stopAnimating()
|
||||
|
||||
case .error:
|
||||
self.collectionView.isHidden = true
|
||||
noResultsView.isHidden = true
|
||||
searchErrorView.isHidden = false
|
||||
activityIndicator.isHidden = true
|
||||
activityIndicator.stopAnimating()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -314,7 +316,6 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
// MARK: - UICollectionViewDelegate
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
|
||||
guard let cell = collectionView.cellForItem(at: indexPath) as? GifPickerCell else {
|
||||
owsFailDebug("unexpected cell.")
|
||||
return
|
||||
|
@ -345,7 +346,7 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
|
||||
layer.path = path.cgPath
|
||||
layer.fillRule = .evenOdd
|
||||
layer.fillColor = UIColor.black.cgColor
|
||||
layer.themeFillColor = .black
|
||||
layer.opacity = 0.7
|
||||
}
|
||||
maskingView.autoPinEdgesToSuperviewEdges()
|
||||
|
@ -389,9 +390,11 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
return
|
||||
}
|
||||
|
||||
let alert = UIAlertController(title: NSLocalizedString("GIF_PICKER_FAILURE_ALERT_TITLE", comment: "Shown when selected GIF couldn't be fetched"),
|
||||
message: error.localizedDescription,
|
||||
preferredStyle: .alert)
|
||||
let alert = UIAlertController(
|
||||
title: "GIF_PICKER_FAILURE_ALERT_TITLE".localized(),
|
||||
message: error.localizedDescription,
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: CommonStrings.retryButton, style: .default) { _ in
|
||||
strongSelf.getFileForCell(cell)
|
||||
})
|
||||
|
@ -439,11 +442,7 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
progressiveSearchTimer = nil
|
||||
let kProgressiveSearchDelaySeconds = 1.0
|
||||
progressiveSearchTimer = WeakTimer.scheduledTimer(timeInterval: kProgressiveSearchDelaySeconds, target: self, userInfo: nil, repeats: true) { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.tryToSearch()
|
||||
self?.tryToSearch()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -457,19 +456,24 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
progressiveSearchTimer?.invalidate()
|
||||
progressiveSearchTimer = nil
|
||||
|
||||
guard let text = searchBar.text else {
|
||||
guard let text: String = searchBar.text else {
|
||||
// Alert message shown when user tries to search for GIFs without entering any search terms
|
||||
OWSAlerts.showErrorAlert(message: "GIF_PICKER_VIEW_MISSING_QUERY".localized())
|
||||
return
|
||||
}
|
||||
|
||||
let query = (text as String).trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||||
|
||||
let query: String = text.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
if (viewMode == .searching || viewMode == .results) && lastQuery == query {
|
||||
Logger.info("ignoring duplicate search: \(query)")
|
||||
return
|
||||
}
|
||||
|
||||
guard !query.isEmpty else {
|
||||
loadTrending()
|
||||
return
|
||||
}
|
||||
|
||||
search(query: query)
|
||||
}
|
||||
|
||||
|
@ -477,20 +481,22 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
assert(progressiveSearchTimer == nil)
|
||||
assert(searchBar.text == nil || searchBar.text?.count == 0)
|
||||
|
||||
GiphyAPI.sharedInstance.trending().done { [weak self] imageInfos in
|
||||
guard let self = self else { return }
|
||||
|
||||
Logger.info("showing trending")
|
||||
if imageInfos.count > 0 {
|
||||
self.imageInfos = imageInfos
|
||||
self.viewMode = .results
|
||||
} else {
|
||||
owsFailDebug("trending results was unexpectedly empty")
|
||||
GiphyAPI.sharedInstance.trending()
|
||||
.done { [weak self] imageInfos in
|
||||
Logger.info("showing trending")
|
||||
|
||||
if imageInfos.count > 0 {
|
||||
self?.imageInfos = imageInfos
|
||||
self?.viewMode = .results
|
||||
}
|
||||
else {
|
||||
owsFailDebug("trending results was unexpectedly empty")
|
||||
}
|
||||
}
|
||||
.catch { error in
|
||||
// Don't both showing error UI feedback for default "trending" results.
|
||||
Logger.error("error: \(error)")
|
||||
}
|
||||
}.catch { error in
|
||||
// Don't both showing error UI feedback for default "trending" results.
|
||||
Logger.error("error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func search(query: String) {
|
||||
|
@ -503,22 +509,26 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
lastQuery = query
|
||||
self.collectionView.contentOffset = CGPoint.zero
|
||||
|
||||
GiphyAPI.sharedInstance.search(query: query, success: { [weak self] imageInfos in
|
||||
guard let strongSelf = self else { return }
|
||||
Logger.info("search complete")
|
||||
strongSelf.imageInfos = imageInfos
|
||||
if imageInfos.count > 0 {
|
||||
strongSelf.viewMode = .results
|
||||
} else {
|
||||
strongSelf.viewMode = .noResults
|
||||
}
|
||||
},
|
||||
failure: { [weak self] _ in
|
||||
guard let strongSelf = self else { return }
|
||||
Logger.info("search failed.")
|
||||
// TODO: Present this error to the user.
|
||||
strongSelf.viewMode = .error
|
||||
})
|
||||
GiphyAPI.sharedInstance
|
||||
.search(
|
||||
query: query,
|
||||
success: { [weak self] imageInfos in
|
||||
Logger.info("search complete")
|
||||
self?.imageInfos = imageInfos
|
||||
|
||||
if imageInfos.count > 0 {
|
||||
self?.viewMode = .results
|
||||
}
|
||||
else {
|
||||
self?.viewMode = .noResults
|
||||
}
|
||||
},
|
||||
failure: { [weak self] _ in
|
||||
Logger.info("search failed.")
|
||||
// TODO: Present this error to the user.
|
||||
self?.viewMode = .error
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - GifPickerLayoutDelegate
|
||||
|
|
|
@ -6,6 +6,7 @@ import Foundation
|
|||
import Photos
|
||||
import PromiseKit
|
||||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
protocol ImagePickerGridControllerDelegate: AnyObject {
|
||||
func imagePickerDidCompleteSelection(_ imagePicker: ImagePickerGridController)
|
||||
|
@ -48,7 +49,7 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.view.backgroundColor = Colors.navigationBarBackground
|
||||
self.view.themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
library.add(delegate: self)
|
||||
|
||||
|
@ -71,7 +72,7 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
|
|||
let cancelImage = UIImage(imageLiteralResourceName: "X")
|
||||
let cancelButton = UIBarButtonItem(image: cancelImage, style: .plain, target: self, action: #selector(didPressCancel))
|
||||
|
||||
cancelButton.tintColor = Colors.text
|
||||
cancelButton.themeTintColor = .textPrimary
|
||||
navigationItem.leftBarButtonItem = cancelButton
|
||||
|
||||
let titleView = TitleView()
|
||||
|
@ -80,7 +81,7 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
|
|||
navigationItem.titleView = titleView
|
||||
self.titleView = titleView
|
||||
|
||||
collectionView.backgroundColor = Colors.navigationBarBackground
|
||||
collectionView.themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
let selectionPanGesture = DirectionalPanGestureRecognizer(direction: [.horizontal], target: self, action: #selector(didPanSelection))
|
||||
selectionPanGesture.delegate = self
|
||||
|
@ -105,7 +106,9 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
|
|||
enum BatchSelectionGestureMode {
|
||||
case select, deselect
|
||||
}
|
||||
|
||||
var selectionPanGestureMode: BatchSelectionGestureMode = .select
|
||||
var hasEverAppeared: Bool = false
|
||||
|
||||
@objc
|
||||
func didPanSelection(_ selectionPanGesture: UIPanGestureRecognizer) {
|
||||
|
@ -189,20 +192,9 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
|
|||
super.viewWillLayoutSubviews()
|
||||
updateLayout()
|
||||
}
|
||||
|
||||
var hasEverAppeared: Bool = false
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
let backgroundImage: UIImage = UIImage(color: Colors.navigationBarBackground)
|
||||
self.navigationItem.title = nil
|
||||
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
|
||||
self.navigationController?.navigationBar.shadowImage = UIImage()
|
||||
self.navigationController?.navigationBar.isTranslucent = false
|
||||
self.navigationController?.navigationBar.barTintColor = Colors.navigationBarBackground
|
||||
(self.navigationController?.navigationBar as? OWSNavigationBar)?.respectsTheme = true
|
||||
self.navigationController?.navigationBar.backgroundColor = Colors.navigationBarBackground
|
||||
self.navigationController?.navigationBar.setBackgroundImage(backgroundImage, for: .default)
|
||||
|
||||
// Determine the size of the thumbnails to request
|
||||
let scale = UIScreen.main.scale
|
||||
|
@ -362,9 +354,7 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
|
|||
// MARK: - Batch Selection
|
||||
|
||||
func batchSelectModeDidChange() {
|
||||
guard let delegate = delegate else {
|
||||
return
|
||||
}
|
||||
guard let delegate = delegate else { return }
|
||||
|
||||
guard let collectionView = collectionView else {
|
||||
owsFailDebug("collectionView was unexpectedly nil")
|
||||
|
@ -397,7 +387,7 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
|
|||
|
||||
let toastText = String(format: toastFormat, NSNumber(value: SignalAttachment.maxAttachmentsAllowed))
|
||||
|
||||
let toastController = ToastController(text: toastText)
|
||||
let toastController = ToastController(text: toastText, background: .backgroundPrimary)
|
||||
|
||||
let kToastInset: CGFloat = 10
|
||||
let bottomInset = kToastInset + collectionView.contentInset.bottom + view.layoutMargins.bottom
|
||||
|
@ -531,8 +521,6 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
|
|||
}
|
||||
|
||||
let cell: PhotoGridViewCell = collectionView.dequeue(type: PhotoGridViewCell.self, for: indexPath)
|
||||
cell.loadingColor = UIColor(white: 0.2, alpha: 1)
|
||||
|
||||
let assetItem = photoCollectionContents.assetItem(at: indexPath.item, photoMediaSize: photoMediaSize)
|
||||
cell.configure(item: assetItem)
|
||||
|
||||
|
@ -586,12 +574,12 @@ class TitleView: UIView {
|
|||
|
||||
addSubview(stackView)
|
||||
stackView.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
label.textColor = Colors.text
|
||||
|
||||
label.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
||||
label.themeTextColor = .textPrimary
|
||||
|
||||
iconView.tintColor = Colors.text
|
||||
iconView.image = UIImage(named: "navbar_disclosure_down")?.withRenderingMode(.alwaysTemplate)
|
||||
iconView.themeTintColor = .textPrimary
|
||||
|
||||
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(titleTapped)))
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ class MediaDetailViewController: OWSViewController, UIScrollViewDelegate, OWSVid
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.view.backgroundColor = Colors.navigationBarBackground
|
||||
self.view.themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
self.view.addSubview(scrollView)
|
||||
scrollView.pin(to: self.view)
|
||||
|
@ -185,13 +185,13 @@ class MediaDetailViewController: OWSViewController, UIScrollViewDelegate, OWSVid
|
|||
}
|
||||
else {
|
||||
self.mediaView = UIView()
|
||||
self.mediaView.backgroundColor = Colors.unimportant
|
||||
self.mediaView.themeBackgroundColor = .backgroundSecondary
|
||||
}
|
||||
}
|
||||
else if self.image == nil {
|
||||
// Still loading thumbnail.
|
||||
self.mediaView = UIView()
|
||||
self.mediaView.backgroundColor = Colors.unimportant
|
||||
self.mediaView.themeBackgroundColor = .backgroundSecondary
|
||||
}
|
||||
else if self.galleryItem.attachment.isVideo {
|
||||
if self.galleryItem.attachment.isValid {
|
||||
|
@ -199,7 +199,7 @@ class MediaDetailViewController: OWSViewController, UIScrollViewDelegate, OWSVid
|
|||
}
|
||||
else {
|
||||
self.mediaView = UIView()
|
||||
self.mediaView.backgroundColor = Colors.unimportant
|
||||
self.mediaView.themeBackgroundColor = .backgroundSecondary
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -16,7 +16,7 @@ class MediaGalleryNavigationController: OWSNavigationController {
|
|||
|
||||
private lazy var backgroundView: UIView = {
|
||||
let result: UIView = UIView()
|
||||
result.backgroundColor = Colors.navigationBarBackground
|
||||
result.themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
return result
|
||||
}()
|
||||
|
@ -27,18 +27,8 @@ class MediaGalleryNavigationController: OWSNavigationController {
|
|||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
guard let navigationBar = self.navigationBar as? OWSNavigationBar else {
|
||||
owsFailDebug("navigationBar had unexpected class: \(self.navigationBar)")
|
||||
return
|
||||
}
|
||||
|
||||
view.backgroundColor = Colors.navigationBarBackground
|
||||
|
||||
navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
|
||||
navigationBar.shadowImage = UIImage()
|
||||
navigationBar.isTranslucent = false
|
||||
navigationBar.barTintColor = Colors.navigationBarBackground
|
||||
|
||||
view.themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
// Insert a view to ensure the nav bar colour goes to the top of the screen
|
||||
relayoutBackgroundView()
|
||||
|
|
|
@ -402,6 +402,7 @@ public class MediaGalleryViewModel {
|
|||
.baseQuery(
|
||||
orderSQL: SQL(interactionAttachment[.albumIndex]),
|
||||
customFilters: SQL("""
|
||||
\(attachment[.isVisualMedia]) = true AND
|
||||
\(attachment[.isValid]) = true AND
|
||||
\(interaction[.id]) = \(interactionId)
|
||||
""")
|
||||
|
@ -416,6 +417,8 @@ public class MediaGalleryViewModel {
|
|||
.baseQuery(
|
||||
orderSQL: Item.galleryReverseOrderSQL,
|
||||
customFilters: SQL("""
|
||||
\(attachment[.isVisualMedia]) = true AND
|
||||
\(attachment[.isValid]) = true AND
|
||||
\(interaction[.timestampMs]) > \(albumTimestampMs) AND
|
||||
\(interaction[.threadId]) = \(threadId)
|
||||
""")
|
||||
|
@ -425,6 +428,8 @@ public class MediaGalleryViewModel {
|
|||
.baseQuery(
|
||||
orderSQL: Item.galleryOrderSQL,
|
||||
customFilters: SQL("""
|
||||
\(attachment[.isVisualMedia]) = true AND
|
||||
\(attachment[.isValid]) = true AND
|
||||
\(interaction[.timestampMs]) < \(albumTimestampMs) AND
|
||||
\(interaction[.threadId]) = \(threadId)
|
||||
""")
|
||||
|
|
|
@ -32,6 +32,17 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
owsFailDebug("unexpectedly unable to build new gallery page")
|
||||
return
|
||||
}
|
||||
|
||||
// Cache and retrieve the new album items
|
||||
viewModel.loadAndCacheAlbumData(
|
||||
for: item.interactionId,
|
||||
in: self.viewModel.threadId
|
||||
)
|
||||
|
||||
// Swap out the database observer
|
||||
dataChangeObservable?.cancel()
|
||||
viewModel.replaceAlbumObservation(toObservationFor: item.interactionId)
|
||||
startObservingChanges()
|
||||
|
||||
updateTitle(item: item)
|
||||
updateCaption(item: item)
|
||||
|
@ -93,12 +104,12 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
var footerBar: UIToolbar = {
|
||||
let result: UIToolbar = UIToolbar()
|
||||
result.clipsToBounds = true // hide 1px top-border
|
||||
result.tintColor = Colors.text
|
||||
result.barTintColor = Colors.navigationBarBackground
|
||||
result.themeTintColor = .textPrimary
|
||||
result.themeBarTintColor = .backgroundPrimary
|
||||
result.themeBackgroundColor = .backgroundPrimary
|
||||
result.setBackgroundImage(UIImage(), forToolbarPosition: .any, barMetrics: UIBarMetrics.default)
|
||||
result.setShadowImage(UIImage(), forToolbarPosition: .any)
|
||||
result.isTranslucent = false
|
||||
result.backgroundColor = Colors.navigationBarBackground
|
||||
|
||||
return result
|
||||
}()
|
||||
|
@ -115,7 +126,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
|
||||
// Navigation
|
||||
|
||||
let backButton = OWSViewController.createOWSBackButton(withTarget: self, selector: #selector(didPressDismissButton))
|
||||
let backButton = UIViewController.createOWSBackButton(target: self, selector: #selector(didPressDismissButton))
|
||||
self.navigationItem.leftBarButtonItem = backButton
|
||||
self.navigationItem.titleView = portraitHeaderView
|
||||
|
||||
|
@ -154,9 +165,9 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
}
|
||||
|
||||
// Views
|
||||
pagerScrollView.backgroundColor = Colors.navigationBarBackground
|
||||
pagerScrollView.themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
view.backgroundColor = Colors.navigationBarBackground
|
||||
view.themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
captionContainerView.delegate = self
|
||||
updateCaptionContainerVisibility()
|
||||
|
@ -169,7 +180,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
let bottomContainer: DynamicallySizedView = DynamicallySizedView()
|
||||
bottomContainer.clipsToBounds = true
|
||||
bottomContainer.autoresizingMask = .flexibleHeight
|
||||
bottomContainer.backgroundColor = Colors.navigationBarBackground
|
||||
bottomContainer.themeBackgroundColor = .backgroundPrimary
|
||||
self.bottomContainer = bottomContainer
|
||||
|
||||
let bottomStack = UIStackView(arrangedSubviews: [captionContainerView, galleryRailView, footerBar])
|
||||
|
@ -179,7 +190,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
bottomStack.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
let galleryRailBlockingView: UIView = UIView()
|
||||
galleryRailBlockingView.backgroundColor = Colors.navigationBarBackground
|
||||
galleryRailBlockingView.themeBackgroundColor = .backgroundPrimary
|
||||
bottomStack.addSubview(galleryRailBlockingView)
|
||||
galleryRailBlockingView.pin(.top, to: .bottom, of: footerBar)
|
||||
galleryRailBlockingView.pin(.left, to: .left, of: bottomStack)
|
||||
|
@ -196,12 +207,6 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
let verticalSwipe = UISwipeGestureRecognizer(target: self, action: #selector(didSwipeView))
|
||||
verticalSwipe.direction = [.up, .down]
|
||||
view.addGestureRecognizer(verticalSwipe)
|
||||
|
||||
let navigationBar = navigationController!.navigationBar
|
||||
navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
|
||||
navigationBar.shadowImage = UIImage()
|
||||
navigationBar.isTranslucent = false
|
||||
navigationBar.barTintColor = Colors.navigationBarBackground
|
||||
|
||||
// Notifications
|
||||
NotificationCenter.default.addObserver(
|
||||
|
@ -316,7 +321,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
target: self,
|
||||
action: #selector(didPressShare)
|
||||
)
|
||||
shareBarButton.tintColor = Colors.text
|
||||
shareBarButton.themeTintColor = .textPrimary
|
||||
|
||||
return shareBarButton
|
||||
}()
|
||||
|
@ -327,7 +332,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
target: self,
|
||||
action: #selector(didPressDelete)
|
||||
)
|
||||
deleteBarButton.tintColor = Colors.text
|
||||
deleteBarButton.themeTintColor = .textPrimary
|
||||
|
||||
return deleteBarButton
|
||||
}()
|
||||
|
@ -342,7 +347,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
target: self,
|
||||
action: #selector(didPressPlayBarButton)
|
||||
)
|
||||
videoPlayBarButton.tintColor = Colors.text
|
||||
videoPlayBarButton.themeTintColor = .textPrimary
|
||||
|
||||
return videoPlayBarButton
|
||||
}()
|
||||
|
@ -353,7 +358,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
target: self,
|
||||
action: #selector(didPressPauseBarButton)
|
||||
)
|
||||
videoPauseBarButton.tintColor = Colors.text
|
||||
videoPauseBarButton.themeTintColor = .textPrimary
|
||||
|
||||
return videoPauseBarButton
|
||||
}()
|
||||
|
@ -818,9 +823,9 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
}()
|
||||
|
||||
lazy private var portraitHeaderNameLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = Colors.text
|
||||
let label: UILabel = UILabel()
|
||||
label.font = .systemFont(ofSize: Values.mediumFontSize)
|
||||
label.themeTextColor = .textPrimary
|
||||
label.textAlignment = .center
|
||||
label.adjustsFontSizeToFitWidth = true
|
||||
label.minimumScaleFactor = 0.8
|
||||
|
@ -829,9 +834,9 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
}()
|
||||
|
||||
lazy private var portraitHeaderDateLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = Colors.text
|
||||
let label: UILabel = UILabel()
|
||||
label.font = .systemFont(ofSize: Values.verySmallFontSize)
|
||||
label.themeTextColor = .textPrimary
|
||||
label.textAlignment = .center
|
||||
label.adjustsFontSizeToFitWidth = true
|
||||
label.minimumScaleFactor = 0.8
|
||||
|
@ -840,7 +845,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
}()
|
||||
|
||||
private lazy var portraitHeaderView: UIView = {
|
||||
let stackView = UIStackView()
|
||||
let stackView: UIStackView = UIStackView()
|
||||
stackView.axis = .vertical
|
||||
stackView.alignment = .center
|
||||
stackView.spacing = 0
|
||||
|
@ -915,12 +920,15 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
|
||||
extension MediaGalleryViewModel.Item: GalleryRailItem {
|
||||
public func buildRailItemView() -> UIView {
|
||||
let imageView = UIImageView()
|
||||
let imageView: UIImageView = UIImageView()
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
getRailImage().map { [weak imageView] image in
|
||||
guard let imageView = imageView else { return }
|
||||
imageView.image = image
|
||||
}.retainUntilComplete()
|
||||
|
||||
getRailImage()
|
||||
.map { [weak imageView] image in
|
||||
guard let imageView = imageView else { return }
|
||||
imageView.image = image
|
||||
}
|
||||
.retainUntilComplete()
|
||||
|
||||
return imageView
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour
|
|||
lazy var collectionView: UICollectionView = {
|
||||
let result: UICollectionView = UICollectionView(frame: .zero, collectionViewLayout: mediaTileViewLayout)
|
||||
result.translatesAutoresizingMaskIntoConstraints = false
|
||||
result.backgroundColor = Colors.navigationBarBackground
|
||||
result.themeBackgroundColor = .backgroundSecondary
|
||||
result.delegate = self
|
||||
result.dataSource = self
|
||||
result.register(view: PhotoGridViewCell.self)
|
||||
|
@ -95,8 +95,8 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour
|
|||
animated: false
|
||||
)
|
||||
|
||||
result.barTintColor = Colors.navigationBarBackground
|
||||
result.tintColor = Colors.text
|
||||
result.themeBarTintColor = .backgroundPrimary
|
||||
result.themeTintColor = .textPrimary
|
||||
|
||||
return result
|
||||
}()
|
||||
|
@ -107,19 +107,21 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour
|
|||
target: self,
|
||||
action: #selector(didPressDelete)
|
||||
)
|
||||
result.tintColor = Colors.text
|
||||
result.themeTintColor = .textPrimary
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
|
||||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
// Add a custom back button if this is the only view controller
|
||||
if self.navigationController?.viewControllers.first == self {
|
||||
let backButton = OWSViewController.createOWSBackButton(withTarget: self, selector: #selector(didPressDismissButton))
|
||||
let backButton = UIViewController.createOWSBackButton(target: self, selector: #selector(didPressDismissButton))
|
||||
self.navigationItem.leftBarButtonItem = backButton
|
||||
}
|
||||
|
||||
|
@ -763,25 +765,21 @@ private class MediaGallerySectionHeader: UICollectionReusableView {
|
|||
|
||||
override init(frame: CGRect) {
|
||||
label = UILabel()
|
||||
label.textColor = Colors.text
|
||||
|
||||
let blurEffect = UIBlurEffect(style: .dark)
|
||||
let blurEffectView = UIVisualEffectView(effect: blurEffect)
|
||||
|
||||
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
label.themeTextColor = .textPrimary
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.backgroundColor = isLightMode ? Colors.cellBackground : UIColor.ows_black.withAlphaComponent(OWSNavigationBar.backgroundBlurMutingFactor)
|
||||
self.themeBackgroundColor = .clear
|
||||
|
||||
let backgroundView: UIView = UIView()
|
||||
backgroundView.themeBackgroundColor = .backgroundSecondary
|
||||
addSubview(backgroundView)
|
||||
backgroundView.pin(to: self)
|
||||
|
||||
self.addSubview(blurEffectView)
|
||||
self.addSubview(label)
|
||||
|
||||
blurEffectView.autoPinEdgesToSuperviewEdges()
|
||||
blurEffectView.isHidden = isLightMode
|
||||
label.autoPinEdge(toSuperviewMargin: .trailing)
|
||||
label.autoPinEdge(toSuperviewMargin: .leading)
|
||||
label.autoVCenterInSuperview()
|
||||
label.pin(.leading, to: .leading, of: self, withInset: Values.largeSpacing)
|
||||
label.pin(.trailing, to: .trailing, of: self, withInset: -Values.largeSpacing)
|
||||
label.center(.vertical, in: self)
|
||||
}
|
||||
|
||||
@available(*, unavailable, message: "Unimplemented")
|
||||
|
@ -811,7 +809,7 @@ private class MediaGalleryStaticHeader: UICollectionViewCell {
|
|||
|
||||
addSubview(label)
|
||||
|
||||
label.textColor = Colors.text
|
||||
label.themeTextColor = .textPrimary
|
||||
label.textAlignment = .center
|
||||
label.numberOfLines = 0
|
||||
label.autoPinEdgesToSuperviewMargins(with: UIEdgeInsets(top: 0, leading: Values.largeSpacing, bottom: 0, trailing: Values.largeSpacing))
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
import PromiseKit
|
||||
import CoreServices
|
||||
|
||||
|
@ -54,9 +55,9 @@ class PhotoCapture: NSObject {
|
|||
self.session.beginConfiguration()
|
||||
defer { self.session.commitConfiguration() }
|
||||
|
||||
let audioDevice = AVCaptureDevice.default(for: .audio)
|
||||
guard let audioDevice: AVCaptureDevice = AVCaptureDevice.default(for: .audio) else { return }
|
||||
// verify works without audio permissions
|
||||
let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice!)
|
||||
let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice)
|
||||
if session.canAddInput(audioDeviceInput) {
|
||||
// self.session.addInputWithNoConnections(audioDeviceInput)
|
||||
session.addInput(audioDeviceInput)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
import PromiseKit
|
||||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
protocol PhotoCaptureViewControllerDelegate: AnyObject {
|
||||
func photoCaptureViewController(_ photoCaptureViewController: PhotoCaptureViewController, didFinishProcessingAttachment attachment: SignalAttachment)
|
||||
|
@ -49,7 +49,7 @@ class PhotoCaptureViewController: OWSViewController {
|
|||
|
||||
override func loadView() {
|
||||
self.view = UIView()
|
||||
self.view.backgroundColor = .black
|
||||
self.view.themeBackgroundColor = .backgroundSecondary
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
@ -82,7 +82,8 @@ class PhotoCaptureViewController: OWSViewController {
|
|||
navigationItem.rightBarButtonItems = nil
|
||||
navigationItem.titleView = recordingTimerView
|
||||
recordingTimerView.sizeToFit()
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
navigationItem.titleView = nil
|
||||
navigationItem.leftBarButtonItem = dismissControl.barButtonItem
|
||||
let fixedSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
|
||||
|
@ -114,8 +115,9 @@ class PhotoCaptureViewController: OWSViewController {
|
|||
let barButtonItem: UIBarButtonItem
|
||||
|
||||
init(imageName: String, block: @escaping () -> Void) {
|
||||
self.button = OWSButton(imageName: imageName, tintColor: .ows_white, block: block)
|
||||
self.button = OWSButton(imageName: imageName, tintColor: .white, block: block)
|
||||
button.autoPinToSquareAspectRatio()
|
||||
button.themeShadowColor = .black
|
||||
button.layer.shadowOffset = CGSize.zero
|
||||
button.layer.shadowOpacity = 0.35
|
||||
button.layer.shadowRadius = 4
|
||||
|
@ -222,10 +224,12 @@ class PhotoCaptureViewController: OWSViewController {
|
|||
private func setupOrientationMonitoring() {
|
||||
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
|
||||
|
||||
NotificationCenter.default.addObserver(self,
|
||||
selector: #selector(didChangeDeviceOrientation),
|
||||
name: UIDevice.orientationDidChangeNotification,
|
||||
object: UIDevice.current)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(didChangeDeviceOrientation),
|
||||
name: UIDevice.orientationDidChangeNotification,
|
||||
object: UIDevice.current
|
||||
)
|
||||
}
|
||||
|
||||
var lastKnownCaptureOrientation: AVCaptureVideoOrientation = .portrait
|
||||
|
@ -282,15 +286,14 @@ class PhotoCaptureViewController: OWSViewController {
|
|||
captureButton.delegate = photoCapture
|
||||
previewView = CapturePreviewView(session: photoCapture.session)
|
||||
|
||||
photoCapture.startCapture().done { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.showCaptureUI()
|
||||
}.catch { [weak self] error in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.showFailureUI(error: error)
|
||||
}.retainUntilComplete()
|
||||
photoCapture.startCapture()
|
||||
.done { [weak self] in
|
||||
self?.showCaptureUI()
|
||||
}
|
||||
.catch { [weak self] error in
|
||||
self?.showFailureUI(error: error)
|
||||
}
|
||||
.retainUntilComplete()
|
||||
}
|
||||
|
||||
private func showCaptureUI() {
|
||||
|
@ -309,11 +312,17 @@ class PhotoCaptureViewController: OWSViewController {
|
|||
|
||||
private func showFailureUI(error: Error) {
|
||||
Logger.error("error: \(error)")
|
||||
|
||||
OWSAlerts.showAlert(title: nil,
|
||||
message: error.localizedDescription,
|
||||
buttonTitle: CommonStrings.dismissButton,
|
||||
buttonAction: { [weak self] _ in self?.dismiss(animated: true) })
|
||||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: CommonStrings.errorAlertTitle,
|
||||
explanation: error.localizedDescription,
|
||||
cancelTitle: CommonStrings.dismissButton,
|
||||
cancelStyle: .textPrimary,
|
||||
afterClosed: { [weak self] in self?.dismiss(animated: true) }
|
||||
)
|
||||
)
|
||||
|
||||
present(modal, animated: true)
|
||||
}
|
||||
|
||||
private func updateFlashModeControl() {
|
||||
|
@ -421,16 +430,17 @@ class CaptureButton: UIView {
|
|||
|
||||
addSubview(innerButton)
|
||||
innerButtonSizeConstraints = autoSetDimensions(to: CGSize(width: defaultDiameter, height: defaultDiameter))
|
||||
innerButton.backgroundColor = UIColor.ows_white.withAlphaComponent(0.33)
|
||||
innerButton.themeBackgroundColor = .white
|
||||
innerButton.layer.shadowOffset = .zero
|
||||
innerButton.layer.shadowOpacity = 0.33
|
||||
innerButton.layer.shadowRadius = 2
|
||||
innerButton.alpha = 0.33
|
||||
innerButton.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
addSubview(zoomIndicator)
|
||||
zoomIndicatorSizeConstraints = zoomIndicator.autoSetDimensions(to: CGSize(width: defaultDiameter, height: defaultDiameter))
|
||||
zoomIndicator.isUserInteractionEnabled = false
|
||||
zoomIndicator.layer.borderColor = UIColor.ows_white.cgColor
|
||||
zoomIndicator.themeBorderColor = .white
|
||||
zoomIndicator.layer.borderWidth = 1.5
|
||||
zoomIndicator.autoAlignAxis(.horizontal, toSameAxisOf: innerButton)
|
||||
zoomIndicator.autoAlignAxis(.vertical, toSameAxisOf: innerButton)
|
||||
|
@ -569,10 +579,10 @@ class RecordingTimerView: UIView {
|
|||
// MARK: - Subviews
|
||||
|
||||
private lazy var label: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFont.ows_monospacedDigitFont(withSize: 20)
|
||||
let label: UILabel = UILabel()
|
||||
label.font = .ows_monospacedDigitFont(withSize: 20)
|
||||
label.themeTextColor = .textPrimary
|
||||
label.textAlignment = .center
|
||||
label.textColor = UIColor.white
|
||||
label.layer.shadowOffset = CGSize.zero
|
||||
label.layer.shadowOpacity = 0.35
|
||||
label.layer.shadowRadius = 4
|
||||
|
@ -587,8 +597,7 @@ class RecordingTimerView: UIView {
|
|||
icon.layer.shadowOffset = CGSize.zero
|
||||
icon.layer.shadowOpacity = 0.35
|
||||
icon.layer.shadowRadius = 4
|
||||
|
||||
icon.backgroundColor = .red
|
||||
icon.themeBackgroundColor = .danger
|
||||
icon.autoSetDimensions(to: CGSize(width: iconWidth, height: iconWidth))
|
||||
icon.alpha = 0
|
||||
|
||||
|
@ -601,10 +610,12 @@ class RecordingTimerView: UIView {
|
|||
func startCounting() {
|
||||
recordingStartTime = CACurrentMediaTime()
|
||||
timer = Timer.weakScheduledTimer(withTimeInterval: 0.1, target: self, selector: #selector(updateView), userInfo: nil, repeats: true)
|
||||
UIView.animate(withDuration: 0.5,
|
||||
delay: 0,
|
||||
options: [.autoreverse, .repeat],
|
||||
animations: { self.icon.alpha = 1 })
|
||||
UIView.animate(
|
||||
withDuration: 0.5,
|
||||
delay: 0,
|
||||
options: [.autoreverse, .repeat],
|
||||
animations: { self.icon.alpha = 1 }
|
||||
)
|
||||
}
|
||||
|
||||
func stopCounting() {
|
||||
|
|
|
@ -28,9 +28,9 @@ public class PhotoGridViewCell: UICollectionViewCell {
|
|||
|
||||
private static let videoBadgeImage = #imageLiteral(resourceName: "ic_gallery_badge_video")
|
||||
private static let animatedBadgeImage = #imageLiteral(resourceName: "ic_gallery_badge_gif")
|
||||
private static let selectedBadgeImage = #imageLiteral(resourceName: "selected_blue_circle")
|
||||
private static let selectedBadgeImage = UIImage(systemName: "checkmark.circle.fill")
|
||||
|
||||
public var loadingColor = Colors.unimportant
|
||||
public var loadingColor: ThemeValue = .textSecondary
|
||||
|
||||
override public var isSelected: Bool {
|
||||
didSet {
|
||||
|
@ -52,18 +52,23 @@ public class PhotoGridViewCell: UICollectionViewCell {
|
|||
self.contentTypeBadgeView = UIImageView()
|
||||
contentTypeBadgeView.isHidden = true
|
||||
|
||||
let kSelectedBadgeSize = CGSize(width: 32, height: 32)
|
||||
self.selectedBadgeView = UIImageView()
|
||||
selectedBadgeView.image = PhotoGridViewCell.selectedBadgeImage
|
||||
selectedBadgeView.image = PhotoGridViewCell.selectedBadgeImage?.withRenderingMode(.alwaysTemplate)
|
||||
selectedBadgeView.themeTintColor = .primary
|
||||
selectedBadgeView.themeBorderColor = .textPrimary
|
||||
selectedBadgeView.themeBackgroundColor = .textPrimary
|
||||
selectedBadgeView.isHidden = true
|
||||
selectedBadgeView.layer.cornerRadius = (kSelectedBadgeSize.width / 2)
|
||||
|
||||
self.highlightedView = UIView()
|
||||
highlightedView.alpha = 0.2
|
||||
highlightedView.backgroundColor = Colors.cellSelected
|
||||
highlightedView.themeBackgroundColor = .black
|
||||
highlightedView.isHidden = true
|
||||
|
||||
self.selectedView = UIView()
|
||||
selectedView.alpha = 0.3
|
||||
selectedView.backgroundColor = Colors.cellSelected
|
||||
selectedView.themeBackgroundColor = .black
|
||||
selectedView.isHidden = true
|
||||
|
||||
super.init(frame: frame)
|
||||
|
@ -87,9 +92,8 @@ public class PhotoGridViewCell: UICollectionViewCell {
|
|||
contentTypeBadgeView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 3)
|
||||
contentTypeBadgeView.autoSetDimensions(to: kContentTypeBadgeSize)
|
||||
|
||||
let kSelectedBadgeSize = CGSize(width: 31, height: 31)
|
||||
selectedBadgeView.autoPinEdge(toSuperviewEdge: .trailing, withInset: 0)
|
||||
selectedBadgeView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 0)
|
||||
selectedBadgeView.autoPinEdge(toSuperviewEdge: .trailing, withInset: Values.verySmallSpacing)
|
||||
selectedBadgeView.autoPinEdge(toSuperviewEdge: .bottom, withInset: Values.verySmallSpacing)
|
||||
selectedBadgeView.autoSetDimensions(to: kSelectedBadgeSize)
|
||||
}
|
||||
|
||||
|
@ -102,7 +106,7 @@ public class PhotoGridViewCell: UICollectionViewCell {
|
|||
get { return imageView.image }
|
||||
set {
|
||||
imageView.image = newValue
|
||||
imageView.backgroundColor = newValue == nil ? loadingColor : .clear
|
||||
imageView.themeBackgroundColor = (newValue == nil ? loadingColor : .clear)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -81,9 +81,9 @@ class SendMediaNavigationController: OWSNavigationController {
|
|||
didSet {
|
||||
if oldValue != isInBatchSelectMode {
|
||||
mediaLibraryViewController.batchSelectModeDidChange()
|
||||
guard let topViewController = viewControllers.last else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let topViewController = viewControllers.last else { return }
|
||||
|
||||
updateButtons(topViewController: topViewController)
|
||||
}
|
||||
}
|
||||
|
@ -91,23 +91,26 @@ class SendMediaNavigationController: OWSNavigationController {
|
|||
|
||||
func updateButtons(topViewController: UIViewController) {
|
||||
switch topViewController {
|
||||
case is AttachmentApprovalViewController:
|
||||
batchModeButton.isHidden = true
|
||||
doneButton.isHidden = true
|
||||
cameraModeButton.isHidden = true
|
||||
mediaLibraryModeButton.isHidden = true
|
||||
case is ImagePickerGridController:
|
||||
batchModeButton.isHidden = isInBatchSelectMode
|
||||
doneButton.isHidden = !isInBatchSelectMode || (attachmentDraftCollection.count == 0 && mediaLibrarySelections.count == 0)
|
||||
cameraModeButton.isHidden = false
|
||||
mediaLibraryModeButton.isHidden = true
|
||||
case is PhotoCaptureViewController:
|
||||
batchModeButton.isHidden = isInBatchSelectMode
|
||||
doneButton.isHidden = !isInBatchSelectMode || (attachmentDraftCollection.count == 0 && mediaLibrarySelections.count == 0)
|
||||
cameraModeButton.isHidden = true
|
||||
mediaLibraryModeButton.isHidden = false
|
||||
default:
|
||||
owsFailDebug("unexpected topViewController: \(topViewController)")
|
||||
case is AttachmentApprovalViewController:
|
||||
batchModeButton.isHidden = true
|
||||
doneButton.isHidden = true
|
||||
cameraModeButton.isHidden = true
|
||||
mediaLibraryModeButton.isHidden = true
|
||||
|
||||
case is ImagePickerGridController:
|
||||
batchModeButton.isHidden = isInBatchSelectMode
|
||||
doneButton.isHidden = !isInBatchSelectMode || (attachmentDraftCollection.count == 0 && mediaLibrarySelections.count == 0)
|
||||
cameraModeButton.isHidden = false
|
||||
mediaLibraryModeButton.isHidden = true
|
||||
|
||||
case is PhotoCaptureViewController:
|
||||
batchModeButton.isHidden = isInBatchSelectMode
|
||||
doneButton.isHidden = !isInBatchSelectMode || (attachmentDraftCollection.count == 0 && mediaLibrarySelections.count == 0)
|
||||
cameraModeButton.isHidden = true
|
||||
mediaLibraryModeButton.isHidden = false
|
||||
|
||||
default:
|
||||
owsFailDebug("unexpected topViewController: \(topViewController)")
|
||||
}
|
||||
|
||||
doneButton.updateCount()
|
||||
|
@ -150,52 +153,60 @@ class SendMediaNavigationController: OWSNavigationController {
|
|||
private lazy var doneButton: DoneButton = {
|
||||
let button = DoneButton()
|
||||
button.delegate = self
|
||||
button.setShadow()
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var batchModeButton: UIButton = {
|
||||
let button = OWSButton(imageName: "media_send_batch_mode_disabled",
|
||||
tintColor: .ows_gray60,
|
||||
block: { [weak self] in self?.didTapBatchModeButton() })
|
||||
|
||||
let width: CGFloat = type(of: self).bottomButtonWidth
|
||||
button.autoSetDimensions(to: CGSize(width: width, height: width))
|
||||
button.layer.cornerRadius = width / 2
|
||||
let button = OWSButton(
|
||||
imageName: "media_send_batch_mode_disabled",
|
||||
tintColor: .backgroundPrimary,
|
||||
block: { [weak self] in self?.didTapBatchModeButton() }
|
||||
)
|
||||
button.clipsToBounds = true
|
||||
button.adjustsImageWhenHighlighted = false
|
||||
button.setThemeBackgroundColor(.textPrimary, for: .normal)
|
||||
button.setThemeBackgroundColor(.textSecondary, for: .highlighted)
|
||||
button.imageEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
|
||||
button.backgroundColor = .ows_white
|
||||
button.setShadow()
|
||||
button.layer.cornerRadius = (SendMediaNavigationController.bottomButtonWidth / 2)
|
||||
button.set(.width, to: SendMediaNavigationController.bottomButtonWidth)
|
||||
button.set(.height, to: SendMediaNavigationController.bottomButtonWidth)
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var cameraModeButton: UIButton = {
|
||||
let button = OWSButton(imageName: "settings-avatar-camera-2",
|
||||
tintColor: .ows_gray60,
|
||||
block: { [weak self] in self?.didTapCameraModeButton() })
|
||||
|
||||
let width: CGFloat = type(of: self).bottomButtonWidth
|
||||
button.autoSetDimensions(to: CGSize(width: width, height: width))
|
||||
button.layer.cornerRadius = width / 2
|
||||
let button = OWSButton(
|
||||
imageName: "settings-avatar-camera-2",
|
||||
tintColor: .backgroundPrimary,
|
||||
block: { [weak self] in self?.didTapCameraModeButton() }
|
||||
)
|
||||
button.clipsToBounds = true
|
||||
button.adjustsImageWhenHighlighted = false
|
||||
button.setThemeBackgroundColor(.textPrimary, for: .normal)
|
||||
button.setThemeBackgroundColor(.textSecondary, for: .highlighted)
|
||||
button.imageEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
|
||||
button.backgroundColor = .ows_white
|
||||
button.setShadow()
|
||||
button.layer.cornerRadius = (SendMediaNavigationController.bottomButtonWidth / 2)
|
||||
button.set(.width, to: SendMediaNavigationController.bottomButtonWidth)
|
||||
button.set(.height, to: SendMediaNavigationController.bottomButtonWidth)
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var mediaLibraryModeButton: UIButton = {
|
||||
let button = OWSButton(imageName: "actionsheet_camera_roll_black",
|
||||
tintColor: .ows_gray60,
|
||||
block: { [weak self] in self?.didTapMediaLibraryModeButton() })
|
||||
|
||||
let width: CGFloat = type(of: self).bottomButtonWidth
|
||||
button.autoSetDimensions(to: CGSize(width: width, height: width))
|
||||
button.layer.cornerRadius = width / 2
|
||||
let button = OWSButton(
|
||||
imageName: "actionsheet_camera_roll_black",
|
||||
tintColor: .backgroundPrimary,
|
||||
block: { [weak self] in self?.didTapMediaLibraryModeButton() }
|
||||
)
|
||||
button.clipsToBounds = true
|
||||
button.adjustsImageWhenHighlighted = false
|
||||
button.setThemeBackgroundColor(.textPrimary, for: .normal)
|
||||
button.setThemeBackgroundColor(.textSecondary, for: .highlighted)
|
||||
button.imageEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
|
||||
button.backgroundColor = .ows_white
|
||||
button.setShadow()
|
||||
button.layer.cornerRadius = (SendMediaNavigationController.bottomButtonWidth / 2)
|
||||
button.set(.width, to: SendMediaNavigationController.bottomButtonWidth)
|
||||
button.set(.height, to: SendMediaNavigationController.bottomButtonWidth)
|
||||
|
||||
return button
|
||||
}()
|
||||
|
@ -243,63 +254,45 @@ class SendMediaNavigationController: OWSNavigationController {
|
|||
pushViewController(approvalViewController, animated: true)
|
||||
}
|
||||
|
||||
private func didRequestExit(dontAbandonText: String) {
|
||||
if attachmentDraftCollection.count == 0 {
|
||||
private func didRequestExit() {
|
||||
guard attachmentDraftCollection.count > 0 else {
|
||||
self.sendMediaNavDelegate?.sendMediaNavDidCancel(self)
|
||||
} else {
|
||||
let alertTitle = NSLocalizedString("SEND_MEDIA_ABANDON_TITLE", comment: "alert title when user attempts to leave the send media flow when they have an in-progress album")
|
||||
|
||||
let alert = UIAlertController(title: alertTitle, message: nil, preferredStyle: .alert)
|
||||
|
||||
let confirmAbandonText = NSLocalizedString("SEND_MEDIA_CONFIRM_ABANDON_ALBUM", comment: "alert action, confirming the user wants to exit the media flow and abandon any photos they've taken")
|
||||
let confirmAbandonAction = UIAlertAction(title: confirmAbandonText,
|
||||
style: .destructive,
|
||||
handler: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.sendMediaNavDelegate?.sendMediaNavDidCancel(self)
|
||||
})
|
||||
alert.addAction(confirmAbandonAction)
|
||||
let dontAbandonAction = UIAlertAction(title: dontAbandonText,
|
||||
style: .default,
|
||||
handler: { _ in })
|
||||
alert.addAction(dontAbandonAction)
|
||||
|
||||
self.presentAlert(alert)
|
||||
return
|
||||
}
|
||||
|
||||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "SEND_MEDIA_ABANDON_TITLE".localized(),
|
||||
confirmTitle: "SEND_MEDIA_CONFIRM_ABANDON_ALBUM".localized(),
|
||||
confirmStyle: .danger,
|
||||
cancelStyle: .textPrimary,
|
||||
onConfirm: { [weak self] _ in
|
||||
self?.sendMediaNavDelegate?.sendMediaNavDidCancel(self)
|
||||
}
|
||||
)
|
||||
)
|
||||
self.present(modal, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
extension SendMediaNavigationController: UINavigationControllerDelegate {
|
||||
|
||||
private func setNavBarBackgroundColor(to color: UIColor) {
|
||||
guard let navBar = navigationBar as? OWSNavigationBar else { return }
|
||||
navBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
|
||||
navBar.shadowImage = UIImage()
|
||||
navBar.isTranslucent = false
|
||||
navBar.barTintColor = color
|
||||
}
|
||||
|
||||
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
|
||||
if viewController == captureViewController {
|
||||
setNavBarBackgroundColor(to: .black)
|
||||
} else {
|
||||
setNavBarBackgroundColor(to: Colors.navigationBarBackground)
|
||||
}
|
||||
|
||||
switch viewController {
|
||||
case is PhotoCaptureViewController:
|
||||
if attachmentDraftCollection.count == 1 && !isInBatchSelectMode {
|
||||
// User is navigating "back" to the previous view, indicating
|
||||
// they want to discard the previously captured item
|
||||
discardDraft()
|
||||
}
|
||||
case is ImagePickerGridController:
|
||||
if attachmentDraftCollection.count == 1 && !isInBatchSelectMode {
|
||||
isInBatchSelectMode = true
|
||||
self.mediaLibraryViewController.batchSelectModeDidChange()
|
||||
}
|
||||
default:
|
||||
break
|
||||
case is PhotoCaptureViewController:
|
||||
if attachmentDraftCollection.count == 1 && !isInBatchSelectMode {
|
||||
// User is navigating "back" to the previous view, indicating
|
||||
// they want to discard the previously captured item
|
||||
discardDraft()
|
||||
}
|
||||
|
||||
case is ImagePickerGridController:
|
||||
if attachmentDraftCollection.count == 1 && !isInBatchSelectMode {
|
||||
isInBatchSelectMode = true
|
||||
self.mediaLibraryViewController.batchSelectModeDidChange()
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
self.updateButtons(topViewController: viewController)
|
||||
|
@ -307,30 +300,8 @@ extension SendMediaNavigationController: UINavigationControllerDelegate {
|
|||
|
||||
// In case back navigation was canceled, we re-apply whatever is showing.
|
||||
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
|
||||
if viewController == captureViewController {
|
||||
setNavBarBackgroundColor(to: .black)
|
||||
} else {
|
||||
setNavBarBackgroundColor(to: Colors.navigationBarBackground)
|
||||
}
|
||||
|
||||
self.updateButtons(topViewController: viewController)
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func preferredNavbarTheme(viewController: UIViewController) -> OWSNavigationBar.NavigationBarThemeOverride? {
|
||||
switch viewController {
|
||||
case is AttachmentApprovalViewController:
|
||||
return .clear
|
||||
case is ImagePickerGridController:
|
||||
return .alwaysDark
|
||||
case is PhotoCaptureViewController:
|
||||
return .clear
|
||||
default:
|
||||
owsFailDebug("unexpected viewController: \(viewController)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SendMediaNavigationController: PhotoCaptureViewControllerDelegate {
|
||||
|
@ -338,14 +309,14 @@ extension SendMediaNavigationController: PhotoCaptureViewControllerDelegate {
|
|||
attachmentDraftCollection.append(.camera(attachment: attachment))
|
||||
if isInBatchSelectMode {
|
||||
updateButtons(topViewController: photoCaptureViewController)
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
pushApprovalViewController()
|
||||
}
|
||||
}
|
||||
|
||||
func photoCaptureViewControllerDidCancel(_ photoCaptureViewController: PhotoCaptureViewController) {
|
||||
let dontAbandonText = NSLocalizedString("SEND_MEDIA_RETURN_TO_CAMERA", comment: "alert action when the user decides not to cancel the media flow after all.")
|
||||
didRequestExit(dontAbandonText: dontAbandonText)
|
||||
didRequestExit()
|
||||
}
|
||||
|
||||
func discardDraft() {
|
||||
|
@ -364,8 +335,7 @@ extension SendMediaNavigationController: ImagePickerGridControllerDelegate {
|
|||
}
|
||||
|
||||
func imagePickerDidCancel(_ imagePicker: ImagePickerGridController) {
|
||||
let dontAbandonText = NSLocalizedString("SEND_MEDIA_RETURN_TO_MEDIA_LIBRARY", comment: "alert action when the user decides not to cancel the media flow after all.")
|
||||
didRequestExit(dontAbandonText: dontAbandonText)
|
||||
didRequestExit()
|
||||
}
|
||||
|
||||
func showApprovalAfterProcessingAnyMediaLibrarySelections() {
|
||||
|
@ -374,18 +344,21 @@ extension SendMediaNavigationController: ImagePickerGridControllerDelegate {
|
|||
let backgroundBlock: (ModalActivityIndicatorViewController) -> Void = { modal in
|
||||
let attachmentPromises: [Promise<MediaLibraryAttachment>] = mediaLibrarySelections.map { $0.promise }
|
||||
|
||||
when(fulfilled: attachmentPromises).map { attachments in
|
||||
Logger.debug("built all attachments")
|
||||
modal.dismiss {
|
||||
self.attachmentDraftCollection.selectedFromPicker(attachments: attachments)
|
||||
self.pushApprovalViewController()
|
||||
when(fulfilled: attachmentPromises)
|
||||
.map { attachments in
|
||||
Logger.debug("built all attachments")
|
||||
modal.dismiss {
|
||||
self.attachmentDraftCollection.selectedFromPicker(attachments: attachments)
|
||||
self.pushApprovalViewController()
|
||||
}
|
||||
}
|
||||
}.catch { error in
|
||||
Logger.error("failed to prepare attachments. error: \(error)")
|
||||
modal.dismiss {
|
||||
OWSAlerts.showAlert(title: NSLocalizedString("IMAGE_PICKER_FAILED_TO_PROCESS_ATTACHMENTS", comment: "alert title"))
|
||||
.catch { error in
|
||||
Logger.error("failed to prepare attachments. error: \(error)")
|
||||
modal.dismiss {
|
||||
OWSAlerts.showAlert(title: NSLocalizedString("IMAGE_PICKER_FAILED_TO_PROCESS_ATTACHMENTS", comment: "alert title"))
|
||||
}
|
||||
}
|
||||
}.retainUntilComplete()
|
||||
.retainUntilComplete()
|
||||
}
|
||||
|
||||
ModalActivityIndicatorViewController.present(fromViewController: self,
|
||||
|
@ -398,22 +371,17 @@ extension SendMediaNavigationController: ImagePickerGridControllerDelegate {
|
|||
}
|
||||
|
||||
func imagePicker(_ imagePicker: ImagePickerGridController, didSelectAsset asset: PHAsset, attachmentPromise: Promise<SignalAttachment>) {
|
||||
guard !mediaLibrarySelections.hasValue(forKey: asset) else {
|
||||
return
|
||||
}
|
||||
guard !mediaLibrarySelections.hasValue(forKey: asset) else { return }
|
||||
|
||||
let libraryMedia = MediaLibrarySelection(asset: asset, signalAttachmentPromise: attachmentPromise)
|
||||
mediaLibrarySelections.append(key: asset, value: libraryMedia)
|
||||
|
||||
updateButtons(topViewController: imagePicker)
|
||||
}
|
||||
|
||||
func imagePicker(_ imagePicker: ImagePickerGridController, didDeselectAsset asset: PHAsset) {
|
||||
guard mediaLibrarySelections.hasValue(forKey: asset) else {
|
||||
return
|
||||
}
|
||||
guard mediaLibrarySelections.hasValue(forKey: asset) else { return }
|
||||
|
||||
mediaLibrarySelections.remove(key: asset)
|
||||
|
||||
updateButtons(topViewController: imagePicker)
|
||||
}
|
||||
|
||||
|
@ -599,19 +567,70 @@ private protocol DoneButtonDelegate: AnyObject {
|
|||
|
||||
private class DoneButton: UIView {
|
||||
weak var delegate: DoneButtonDelegate?
|
||||
let numberFormatter: NumberFormatter = NumberFormatter()
|
||||
|
||||
private var didTouchDownInside: Bool = false
|
||||
|
||||
// MARK: - UI
|
||||
|
||||
private let container: UIView = {
|
||||
let result: UIView = UIView()
|
||||
result.themeBackgroundColor = .textPrimary
|
||||
result.layer.cornerRadius = 20
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var badge: CircleView = {
|
||||
let result: CircleView = CircleView()
|
||||
result.themeBackgroundColor = .primary
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var badgeLabel: UILabel = {
|
||||
let result: UILabel = UILabel()
|
||||
result.font = .ows_dynamicTypeSubheadline.ows_monospaced()
|
||||
result.themeTextColor = .black // Will render on the primary color so should always be black
|
||||
result.textAlignment = .center
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var chevron: UIView = {
|
||||
let image: UIImage = {
|
||||
guard CurrentAppContext().isRTL else { return #imageLiteral(resourceName: "small_chevron_right") }
|
||||
|
||||
return #imageLiteral(resourceName: "small_chevron_left")
|
||||
}()
|
||||
let result: UIImageView = UIImageView(image: image.withRenderingMode(.alwaysTemplate))
|
||||
result.contentMode = .scaleAspectFit
|
||||
result.themeTintColor = .backgroundPrimary
|
||||
result.set(.width, to: 10)
|
||||
result.set(.height, to: 18)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap(tapGesture:)))
|
||||
addGestureRecognizer(tapGesture)
|
||||
|
||||
let container = UIView()
|
||||
container.backgroundColor = .ows_white
|
||||
container.layer.cornerRadius = 20
|
||||
container.layoutMargins = UIEdgeInsets(top: 7, leading: 8, bottom: 7, trailing: 8)
|
||||
|
||||
|
||||
addSubview(container)
|
||||
container.autoPinEdgesToSuperviewMargins()
|
||||
container.pin(to: self)
|
||||
|
||||
badge.addSubview(badgeLabel)
|
||||
badgeLabel.pin(to: badge, withInset: 4)
|
||||
|
||||
// Constrain to be a pill that is at least a circle, and maybe wider.
|
||||
badgeLabel.autoPin(toAspectRatio: 1.0, relation: .greaterThanOrEqual)
|
||||
NSLayoutConstraint.autoSetPriority(.defaultLow) {
|
||||
badgeLabel.autoPinToSquareAspectRatio()
|
||||
}
|
||||
|
||||
let stackView = UIStackView(arrangedSubviews: [badge, chevron])
|
||||
stackView.axis = .horizontal
|
||||
|
@ -619,74 +638,78 @@ private class DoneButton: UIView {
|
|||
stackView.spacing = 9
|
||||
|
||||
container.addSubview(stackView)
|
||||
stackView.autoPinEdgesToSuperviewMargins()
|
||||
}
|
||||
|
||||
let numberFormatter: NumberFormatter = NumberFormatter()
|
||||
|
||||
func updateCount() {
|
||||
guard let delegate = delegate else {
|
||||
return
|
||||
}
|
||||
|
||||
badgeLabel.text = numberFormatter.string(for: delegate.doneButtonCount)
|
||||
stackView.pin(.top, to: .top, of: container, withInset: 7)
|
||||
stackView.pin(.leading, to: .leading, of: container, withInset: 8)
|
||||
stackView.pin(.trailing, to: .trailing, of: container, withInset: -8)
|
||||
stackView.pin(.bottom, to: .bottom, of: container, withInset: -7)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
func updateCount() {
|
||||
guard let delegate = delegate else { return }
|
||||
|
||||
// MARK: - Subviews
|
||||
badgeLabel.text = numberFormatter.string(for: delegate.doneButtonCount)
|
||||
}
|
||||
|
||||
// MARK: - Interaction
|
||||
|
||||
private lazy var badge: UIView = {
|
||||
let badge = CircleView()
|
||||
badge.layoutMargins = UIEdgeInsets(top: 4, leading: 4, bottom: 4, trailing: 4)
|
||||
badge.backgroundColor = .ows_signalBlue
|
||||
badge.addSubview(badgeLabel)
|
||||
badgeLabel.autoPinEdgesToSuperviewMargins()
|
||||
|
||||
// Constrain to be a pill that is at least a circle, and maybe wider.
|
||||
badgeLabel.autoPin(toAspectRatio: 1.0, relation: .greaterThanOrEqual)
|
||||
NSLayoutConstraint.autoSetPriority(.defaultLow) {
|
||||
badgeLabel.autoPinToSquareAspectRatio()
|
||||
}
|
||||
|
||||
return badge
|
||||
}()
|
||||
|
||||
private lazy var badgeLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = .ows_white
|
||||
label.font = UIFont.ows_dynamicTypeSubheadline.ows_monospaced()
|
||||
label.textAlignment = .center
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var chevron: UIView = {
|
||||
let image: UIImage
|
||||
if CurrentAppContext().isRTL {
|
||||
image = #imageLiteral(resourceName: "small_chevron_left")
|
||||
} else {
|
||||
image = #imageLiteral(resourceName: "small_chevron_right")
|
||||
}
|
||||
let chevron = UIImageView(image: image.withRenderingMode(.alwaysTemplate))
|
||||
chevron.contentMode = .scaleAspectFit
|
||||
chevron.tintColor = .ows_gray60
|
||||
chevron.autoSetDimensions(to: CGSize(width: 10, height: 18))
|
||||
|
||||
return chevron
|
||||
}()
|
||||
|
||||
@objc
|
||||
func didTap(tapGesture: UITapGestureRecognizer) {
|
||||
@objc func didTap(tapGesture: UITapGestureRecognizer) {
|
||||
delegate?.doneButtonWasTapped(self)
|
||||
}
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
guard
|
||||
isUserInteractionEnabled,
|
||||
let location: CGPoint = touches.first?.location(in: self),
|
||||
bounds.contains(location)
|
||||
else { return }
|
||||
|
||||
didTouchDownInside = true
|
||||
container.themeBackgroundColor = .textSecondary
|
||||
}
|
||||
|
||||
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
guard
|
||||
isUserInteractionEnabled,
|
||||
let location: CGPoint = touches.first?.location(in: self),
|
||||
bounds.contains(location),
|
||||
didTouchDownInside
|
||||
else {
|
||||
if didTouchDownInside {
|
||||
container.themeBackgroundColor = .textPrimary
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
container.themeBackgroundColor = .textSecondary
|
||||
}
|
||||
|
||||
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
if didTouchDownInside {
|
||||
container.themeBackgroundColor = .textPrimary
|
||||
}
|
||||
|
||||
didTouchDownInside = false
|
||||
}
|
||||
|
||||
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
if didTouchDownInside {
|
||||
container.themeBackgroundColor = .textPrimary
|
||||
}
|
||||
|
||||
didTouchDownInside = false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SendMediaNavDelegate
|
||||
|
||||
protocol SendMediaNavDelegate: AnyObject {
|
||||
func sendMediaNavDidCancel(_ sendMediaNavigationController: SendMediaNavigationController)
|
||||
func sendMediaNavDidCancel(_ sendMediaNavigationController: SendMediaNavigationController?)
|
||||
func sendMediaNav(_ sendMediaNavigationController: SendMediaNavigationController, didApproveAttachments attachments: [SignalAttachment], forThreadId threadId: String, messageText: String?)
|
||||
|
||||
func sendMediaNavInitialMessageText(_ sendMediaNavigationController: SendMediaNavigationController) -> String?
|
||||
|
|
|
@ -79,8 +79,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
}
|
||||
)
|
||||
|
||||
SNAppearance.switchToSessionAppearance()
|
||||
|
||||
if Environment.shared?.callManager.wrappedValue?.currentCall == nil {
|
||||
UserDefaults.sharedLokiProject?.set(false, forKey: "isCallOngoing")
|
||||
}
|
||||
|
|
|
@ -236,7 +236,7 @@ NSString *const ReportedApplicationStateDidChangeNotification = @"ReportedApplic
|
|||
- (nullable UIAlertAction *)openSystemSettingsAction
|
||||
{
|
||||
return [UIAlertAction actionWithTitle:CommonStrings.openSettingsButton
|
||||
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"system_settings")
|
||||
accessibilityIdentifier:[NSString stringWithFormat:@"%@.%@", self.class, @"system_settings"]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
[UIApplication.sharedApplication openSystemSettings];
|
||||
|
|
|
@ -13,9 +13,7 @@
|
|||
#import "OWSBezierPathView.h"
|
||||
#import "OWSMessageTimerView.h"
|
||||
#import "OWSNavigationController.h"
|
||||
#import "OWSProgressView.h"
|
||||
#import "OWSWindowManager.h"
|
||||
#import "OWSQRCodeScanningViewController.h"
|
||||
#import "MainAppContext.h"
|
||||
#import "UIViewController+Permissions.h"
|
||||
#import <PureLayout/PureLayout.h>
|
||||
|
@ -31,9 +29,7 @@
|
|||
#import <SignalUtilitiesKit/OWSViewController.h>
|
||||
#import <SignalUtilitiesKit/UIColor+OWS.h>
|
||||
#import <SignalUtilitiesKit/UIFont+OWS.h>
|
||||
#import <SignalUtilitiesKit/UIUtil.h>
|
||||
#import <SessionUtilitiesKit/UIView+OWS.h>
|
||||
#import <SignalUtilitiesKit/UIViewController+OWS.h>
|
||||
#import <SignalUtilitiesKit/AppVersion.h>
|
||||
#import <SessionUtilitiesKit/DataSource.h>
|
||||
#import <SessionUtilitiesKit/MIMETypeUtil.h>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import UIKit
|
||||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
final class DisplayNameVC: BaseVC {
|
||||
private var spacer1HeightConstraint: NSLayoutConstraint!
|
||||
|
@ -13,8 +14,8 @@ final class DisplayNameVC: BaseVC {
|
|||
// MARK: - Components
|
||||
|
||||
private lazy var displayNameTextField: TextField = {
|
||||
let result = TextField(placeholder: NSLocalizedString("vc_display_name_text_field_hint", comment: ""))
|
||||
result.layer.borderColor = Colors.text.cgColor
|
||||
let result = TextField(placeholder: "vc_display_name_text_field_hint".localized())
|
||||
result.themeBorderColor = .textPrimary
|
||||
result.accessibilityLabel = "Display name text field"
|
||||
|
||||
return result
|
||||
|
@ -31,7 +32,7 @@ final class DisplayNameVC: BaseVC {
|
|||
let titleLabel = UILabel()
|
||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||
titleLabel.text = "vc_display_name_title_2".localized()
|
||||
titleLabel.textColor = Colors.text
|
||||
titleLabel.themeTextColor = .textPrimary
|
||||
titleLabel.lineBreakMode = .byWordWrapping
|
||||
titleLabel.numberOfLines = 0
|
||||
|
||||
|
@ -39,7 +40,7 @@ final class DisplayNameVC: BaseVC {
|
|||
let explanationLabel = UILabel()
|
||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
explanationLabel.text = "vc_display_name_explanation".localized()
|
||||
explanationLabel.textColor = Colors.text
|
||||
explanationLabel.themeTextColor = .textPrimary
|
||||
explanationLabel.lineBreakMode = .byWordWrapping
|
||||
explanationLabel.numberOfLines = 0
|
||||
|
||||
|
|
|
@ -66,8 +66,8 @@ final class FakeChatView: UIView {
|
|||
let result = UIView()
|
||||
let bubbleView = UIView()
|
||||
bubbleView.set(.width, to: FakeChatView.bubbleWidth)
|
||||
bubbleView.themeShadowColor = .black
|
||||
bubbleView.layer.cornerRadius = FakeChatView.bubbleCornerRadius
|
||||
bubbleView.layer.shadowColor = UIColor.black.cgColor
|
||||
bubbleView.layer.shadowRadius = isLightMode ? 4 : 8
|
||||
bubbleView.layer.shadowOpacity = isLightMode ? 0.16 : 0.24
|
||||
bubbleView.layer.shadowOffset = CGSize.zero
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
import PromiseKit
|
||||
import SessionUIKit
|
||||
import SessionUtilitiesKit
|
||||
import SessionSnodeKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
final class LinkDeviceVC: BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, OWSQRScannerDelegate {
|
||||
final class LinkDeviceVC: BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, QRScannerDelegate {
|
||||
private let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
|
||||
private var pages: [UIViewController] = []
|
||||
private var targetVCIndex: Int?
|
||||
|
@ -42,29 +44,32 @@ final class LinkDeviceVC: BaseVC, UIPageViewControllerDataSource, UIPageViewCont
|
|||
}()
|
||||
|
||||
private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = {
|
||||
let message = NSLocalizedString("vc_link_device_scan_qr_code_explanation", comment: "")
|
||||
let message = "vc_link_device_scan_qr_code_explanation".localized()
|
||||
let result = ScanQRCodeWrapperVC(message: message)
|
||||
result.delegate = self
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: Lifecycle
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
setNavBarTitle("vc_link_device_title".localized())
|
||||
let navigationBar = navigationController!.navigationBar
|
||||
|
||||
// Page VC
|
||||
let hasCameraAccess = (AVCaptureDevice.authorizationStatus(for: .video) == .authorized)
|
||||
pages = [ recoveryPhraseVC, (hasCameraAccess ? scanQRCodeWrapperVC : scanQRCodePlaceholderVC) ]
|
||||
pageVC.dataSource = self
|
||||
pageVC.delegate = self
|
||||
pageVC.setViewControllers([ recoveryPhraseVC ], direction: .forward, animated: false, completion: nil)
|
||||
|
||||
// Tab bar
|
||||
view.addSubview(tabBar)
|
||||
tabBar.pin(.leading, to: .leading, of: view)
|
||||
tabBarTopConstraint = tabBar.autoPinEdge(toSuperviewSafeArea: .top)
|
||||
view.pin(.trailing, to: .trailing, of: tabBar)
|
||||
|
||||
// Page VC constraints
|
||||
let pageVCView = pageVC.view!
|
||||
view.addSubview(pageVCView)
|
||||
|
@ -72,10 +77,11 @@ final class LinkDeviceVC: BaseVC, UIPageViewControllerDataSource, UIPageViewCont
|
|||
pageVCView.pin(.top, to: .bottom, of: tabBar)
|
||||
view.pin(.trailing, to: .trailing, of: pageVCView)
|
||||
view.pin(.bottom, to: .bottom, of: pageVCView)
|
||||
|
||||
let screen = UIScreen.main.bounds
|
||||
pageVCView.set(.width, to: screen.width)
|
||||
let statusBarHeight = UIApplication.shared.statusBarFrame.height
|
||||
let height = navigationController!.view.bounds.height - navigationBar.height() - TabBar.snHeight - statusBarHeight
|
||||
let height = (navigationController?.view.bounds.height ?? 0) - (navigationController?.navigationBar.bounds.height ?? 0) - TabBar.snHeight - statusBarHeight
|
||||
pageVCView.set(.height, to: height)
|
||||
recoveryPhraseVC.constrainHeight(to: height)
|
||||
scanQRCodePlaceholderVC.constrainHeight(to: height)
|
||||
|
@ -90,7 +96,8 @@ final class LinkDeviceVC: BaseVC, UIPageViewControllerDataSource, UIPageViewCont
|
|||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
// MARK: General
|
||||
// MARK: - General
|
||||
|
||||
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
|
||||
guard let index = pages.firstIndex(of: viewController), index != 0 else { return nil }
|
||||
return pages[index - 1]
|
||||
|
@ -106,7 +113,8 @@ final class LinkDeviceVC: BaseVC, UIPageViewControllerDataSource, UIPageViewCont
|
|||
pageVC.setViewControllers([ scanQRCodeWrapperVC ], direction: .forward, animated: false, completion: nil)
|
||||
}
|
||||
|
||||
// MARK: Updating
|
||||
// MARK: - Updating
|
||||
|
||||
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
|
||||
guard let targetVC = pendingViewControllers.first, let index = pages.firstIndex(of: targetVC) else { return }
|
||||
targetVCIndex = index
|
||||
|
@ -117,27 +125,31 @@ final class LinkDeviceVC: BaseVC, UIPageViewControllerDataSource, UIPageViewCont
|
|||
tabBar.selectTab(at: index)
|
||||
}
|
||||
|
||||
// MARK: Interaction
|
||||
// MARK: - Interaction
|
||||
|
||||
@objc private func close() {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func controller(_ controller: OWSQRCodeScanningViewController, didDetectQRCodeWith string: String) {
|
||||
func controller(_ controller: QRCodeScanningViewController, didDetectQRCodeWith string: String) {
|
||||
let seed = Data(hex: string)
|
||||
continueWithSeed(seed)
|
||||
}
|
||||
|
||||
func continueWithSeed(_ seed: Data) {
|
||||
if (seed.count != 16) {
|
||||
let alert = UIAlertController(
|
||||
title: "invalid_recovery_phrase".localized(),
|
||||
message: "INVALID_RECOVERY_PHRASE_MESSAGE".localized(),
|
||||
preferredStyle: .alert
|
||||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "invalid_recovery_phrase".localized(),
|
||||
explanation: "INVALID_RECOVERY_PHRASE_MESSAGE".localized(),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .textPrimary,
|
||||
afterClosed: { [weak self] in
|
||||
self?.scanQRCodeWrapperVC.startCapture()
|
||||
}
|
||||
)
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: { _ in
|
||||
self.scanQRCodeWrapperVC.startCapture()
|
||||
}))
|
||||
presentAlert(alert)
|
||||
present(modal, animated: true)
|
||||
return
|
||||
}
|
||||
let (ed25519KeyPair, x25519KeyPair) = try! Identity.generate(from: seed)
|
||||
|
@ -164,7 +176,7 @@ final class LinkDeviceVC: BaseVC, UIPageViewControllerDataSource, UIPageViewCont
|
|||
}
|
||||
}
|
||||
|
||||
private final class RecoveryPhraseVC : UIViewController {
|
||||
private final class RecoveryPhraseVC: UIViewController {
|
||||
weak var linkDeviceVC: LinkDeviceVC!
|
||||
private var spacer1HeightConstraint: NSLayoutConstraint!
|
||||
private var spacer2HeightConstraint: NSLayoutConstraint!
|
||||
|
@ -172,21 +184,23 @@ private final class RecoveryPhraseVC : UIViewController {
|
|||
private var bottomConstraint: NSLayoutConstraint!
|
||||
|
||||
private lazy var mnemonicTextView: TextView = {
|
||||
let result = TextView(placeholder: NSLocalizedString("vc_restore_seed_text_field_hint", comment: ""))
|
||||
result.layer.borderColor = Colors.text.cgColor
|
||||
let result = TextView(placeholder: "vc_restore_seed_text_field_hint".localized())
|
||||
result.themeBorderColor = .textPrimary
|
||||
result.accessibilityLabel = "Recovery phrase text view"
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: Lifecycle
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
view.backgroundColor = .clear
|
||||
view.themeBackgroundColor = .clear
|
||||
|
||||
// Title label
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||
titleLabel.text = "vc_enter_recovery_phrase_title".localized()
|
||||
titleLabel.textColor = Colors.text
|
||||
titleLabel.themeTextColor = .textPrimary
|
||||
titleLabel.lineBreakMode = .byWordWrapping
|
||||
titleLabel.numberOfLines = 0
|
||||
|
||||
|
@ -194,7 +208,7 @@ private final class RecoveryPhraseVC : UIViewController {
|
|||
let explanationLabel = UILabel()
|
||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
explanationLabel.text = "vc_enter_recovery_phrase_explanation".localized()
|
||||
explanationLabel.textColor = Colors.text
|
||||
explanationLabel.themeTextColor = .textPrimary
|
||||
explanationLabel.lineBreakMode = .byWordWrapping
|
||||
explanationLabel.numberOfLines = 0
|
||||
|
||||
|
@ -267,7 +281,8 @@ private final class RecoveryPhraseVC : UIViewController {
|
|||
mnemonicTextView.resignFirstResponder()
|
||||
}
|
||||
|
||||
// MARK: Updating
|
||||
// MARK: - Updating
|
||||
|
||||
@objc private func handleKeyboardWillChangeFrameNotification(_ notification: Notification) {
|
||||
guard let newHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height else { return }
|
||||
bottomConstraint.constant = -newHeight // Negative due to how the constraint is set up
|
||||
|
@ -289,11 +304,12 @@ private final class RecoveryPhraseVC : UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: Interaction
|
||||
// MARK: - Interaction
|
||||
|
||||
@objc private func handleContinueButtonTapped() {
|
||||
func showError(title: String, message: String = "") {
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
|
||||
alert.addAction(UIAlertAction(title: "BUTTON_OK".localized(), style: .default, handler: nil))
|
||||
presentAlert(alert)
|
||||
}
|
||||
let mnemonic = mnemonicTextView.text!.lowercased()
|
||||
|
@ -309,31 +325,35 @@ private final class RecoveryPhraseVC : UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
private final class ScanQRCodePlaceholderVC : UIViewController {
|
||||
private final class ScanQRCodePlaceholderVC: UIViewController {
|
||||
weak var linkDeviceVC: LinkDeviceVC!
|
||||
|
||||
override func viewDidLoad() {
|
||||
// Remove background color
|
||||
view.backgroundColor = .clear
|
||||
view.themeBackgroundColor = .clear
|
||||
|
||||
// Set up explanation label
|
||||
let explanationLabel = UILabel()
|
||||
explanationLabel.textColor = Colors.text
|
||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
explanationLabel.text = NSLocalizedString("vc_scan_qr_code_camera_access_explanation", comment: "")
|
||||
explanationLabel.numberOfLines = 0
|
||||
explanationLabel.text = "vc_scan_qr_code_camera_access_explanation".localized()
|
||||
explanationLabel.themeTextColor = .textPrimary
|
||||
explanationLabel.textAlignment = .center
|
||||
explanationLabel.lineBreakMode = .byWordWrapping
|
||||
explanationLabel.numberOfLines = 0
|
||||
|
||||
// Set up call to action button
|
||||
let callToActionButton = UIButton()
|
||||
callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
||||
callToActionButton.setTitleColor(Colors.accent, for: UIControl.State.normal)
|
||||
callToActionButton.setTitle(NSLocalizedString("vc_scan_qr_code_grant_camera_access_button_title", comment: ""), for: UIControl.State.normal)
|
||||
callToActionButton.addTarget(self, action: #selector(requestCameraAccess), for: UIControl.Event.touchUpInside)
|
||||
callToActionButton.titleLabel?.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
||||
callToActionButton.setTitle("vc_scan_qr_code_grant_camera_access_button_title".localized(), for: .normal)
|
||||
callToActionButton.setThemeTitleColor(.primary, for: .normal)
|
||||
callToActionButton.addTarget(self, action: #selector(requestCameraAccess), for: .touchUpInside)
|
||||
|
||||
// Set up stack view
|
||||
let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ])
|
||||
stackView.axis = .vertical
|
||||
stackView.spacing = Values.mediumSpacing
|
||||
stackView.alignment = .center
|
||||
|
||||
// Set up constraints
|
||||
view.set(.width, to: UIScreen.main.bounds.width)
|
||||
view.addSubview(stackView)
|
||||
|
|
|
@ -5,8 +5,9 @@ import PromiseKit
|
|||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
import SessionSnodeKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
final class PNModeVC : BaseVC, OptionViewDelegate {
|
||||
final class PNModeVC: BaseVC, OptionViewDelegate {
|
||||
|
||||
private var optionViews: [OptionView] {
|
||||
[ apnsOptionView, backgroundPollingOptionView ]
|
||||
|
@ -16,36 +17,47 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
|
|||
return optionViews.first { $0.isSelected }
|
||||
}
|
||||
|
||||
// MARK: Components
|
||||
// MARK: - Components
|
||||
|
||||
private lazy var apnsOptionView: OptionView = {
|
||||
let explanation = NSLocalizedString("fast_mode_explanation", comment: "")
|
||||
let result = OptionView(title: "Fast Mode", explanation: explanation, delegate: self, isRecommended: true)
|
||||
let result: OptionView = OptionView(
|
||||
title: "Fast Mode",
|
||||
explanation: "fast_mode_explanation".localized(),
|
||||
delegate: self,
|
||||
isRecommended: true
|
||||
)
|
||||
result.accessibilityLabel = "Fast mode option"
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var backgroundPollingOptionView: OptionView = {
|
||||
let explanation = NSLocalizedString("slow_mode_explanation", comment: "")
|
||||
let result = OptionView(title: "Slow Mode", explanation: explanation, delegate: self)
|
||||
let result: OptionView = OptionView(
|
||||
title: "Slow Mode",
|
||||
explanation: "slow_mode_explanation".localized(),
|
||||
delegate: self
|
||||
)
|
||||
result.accessibilityLabel = "Slow mode option"
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: Lifecycle
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
setUpNavBarSessionIcon()
|
||||
|
||||
let learnMoreButton = UIBarButtonItem(image: #imageLiteral(resourceName: "ic_info"), style: .plain, target: self, action: #selector(learnMore))
|
||||
learnMoreButton.tintColor = Colors.text
|
||||
learnMoreButton.themeTintColor = .textPrimary
|
||||
navigationItem.rightBarButtonItem = learnMoreButton
|
||||
|
||||
// Set up title label
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||
titleLabel.text = "vc_pn_mode_title".localized()
|
||||
titleLabel.textColor = Colors.text
|
||||
titleLabel.themeTextColor = .textPrimary
|
||||
titleLabel.lineBreakMode = .byWordWrapping
|
||||
titleLabel.numberOfLines = 0
|
||||
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
import UIKit
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
final class OptionView : UIView {
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
|
||||
final class OptionView: UIView {
|
||||
private let title: String
|
||||
private let explanation: String
|
||||
private let delegate: OptionViewDelegate
|
||||
private let isRecommended: Bool
|
||||
|
||||
var isSelected = false { didSet { handleIsSelectedChanged() } }
|
||||
|
||||
private static let cornerRadius: CGFloat = 8
|
||||
|
@ -14,7 +18,9 @@ final class OptionView : UIView {
|
|||
self.explanation = explanation
|
||||
self.delegate = delegate
|
||||
self.isRecommended = isRecommended
|
||||
|
||||
super.init(frame: CGRect.zero)
|
||||
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
|
@ -27,49 +33,68 @@ final class OptionView : UIView {
|
|||
}
|
||||
|
||||
private func setUpViewHierarchy() {
|
||||
backgroundColor = Colors.pnOptionBackground
|
||||
themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
// Round corners
|
||||
layer.cornerRadius = OptionView.cornerRadius
|
||||
|
||||
// Set up border
|
||||
themeBorderColor = .borderSeparator
|
||||
layer.borderWidth = 1
|
||||
layer.borderColor = Colors.pnOptionBorder.cgColor
|
||||
|
||||
// Set up shadow
|
||||
layer.shadowColor = UIColor.black.cgColor
|
||||
themeShadowColor = .black
|
||||
layer.shadowOffset = CGSize(width: 0, height: 0.8)
|
||||
layer.shadowOpacity = isLightMode ? 0.16 : 1
|
||||
layer.shadowRadius = isLightMode ? 4 : 6
|
||||
|
||||
ThemeManager.onThemeChange(observer: self) { [weak self] theme, _ in
|
||||
switch theme.interfaceStyle {
|
||||
case .light:
|
||||
self?.layer.shadowOpacity = 0.16
|
||||
self?.layer.shadowRadius = 4
|
||||
|
||||
default:
|
||||
self?.layer.shadowOpacity = 1
|
||||
self?.layer.shadowRadius = 6
|
||||
}
|
||||
}
|
||||
|
||||
// Set up title label
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.textColor = Colors.text
|
||||
let titleLabel: UILabel = UILabel()
|
||||
titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
||||
titleLabel.text = title
|
||||
titleLabel.numberOfLines = 0
|
||||
titleLabel.themeTextColor = .textPrimary
|
||||
titleLabel.lineBreakMode = .byWordWrapping
|
||||
titleLabel.numberOfLines = 0
|
||||
|
||||
// Set up explanation label
|
||||
let explanationLabel = UILabel()
|
||||
explanationLabel.textColor = Colors.text
|
||||
let explanationLabel: UILabel = UILabel()
|
||||
explanationLabel.font = .systemFont(ofSize: Values.verySmallFontSize)
|
||||
explanationLabel.text = explanation
|
||||
explanationLabel.numberOfLines = 0
|
||||
explanationLabel.themeTextColor = .textPrimary
|
||||
explanationLabel.lineBreakMode = .byWordWrapping
|
||||
explanationLabel.numberOfLines = 0
|
||||
|
||||
// Set up stack view
|
||||
let stackView = UIStackView(arrangedSubviews: [ titleLabel, explanationLabel ])
|
||||
stackView.axis = .vertical
|
||||
stackView.spacing = 4
|
||||
stackView.alignment = .fill
|
||||
addSubview(stackView)
|
||||
|
||||
stackView.pin(.leading, to: .leading, of: self, withInset: 12)
|
||||
stackView.pin(.top, to: .top, of: self, withInset: 12)
|
||||
self.pin(.trailing, to: .trailing, of: stackView, withInset: 12)
|
||||
self.pin(.bottom, to: .bottom, of: stackView, withInset: 12)
|
||||
|
||||
// Set up recommended label if needed
|
||||
if isRecommended {
|
||||
let recommendedLabel = UILabel()
|
||||
recommendedLabel.textColor = Colors.accent
|
||||
let recommendedLabel: UILabel = UILabel()
|
||||
recommendedLabel.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
||||
recommendedLabel.text = NSLocalizedString("vc_pn_mode_recommended_option_tag", comment: "")
|
||||
recommendedLabel.text = "vc_pn_mode_recommended_option_tag".localized()
|
||||
recommendedLabel.themeTextColor = .primary
|
||||
stackView.addArrangedSubview(recommendedLabel)
|
||||
}
|
||||
|
||||
// Set up tap gesture recognizer
|
||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
|
||||
addGestureRecognizer(tapGestureRecognizer)
|
||||
|
@ -80,30 +105,18 @@ final class OptionView : UIView {
|
|||
}
|
||||
|
||||
private func handleIsSelectedChanged() {
|
||||
let animationDuration: TimeInterval = 0.25
|
||||
// Animate border color
|
||||
let newBorderColor = isSelected ? Colors.accent.cgColor : Colors.pnOptionBorder.cgColor
|
||||
let borderAnimation = CABasicAnimation(keyPath: "borderColor")
|
||||
borderAnimation.fromValue = layer.shadowColor
|
||||
borderAnimation.toValue = newBorderColor
|
||||
borderAnimation.duration = animationDuration
|
||||
layer.add(borderAnimation, forKey: borderAnimation.keyPath)
|
||||
layer.borderColor = newBorderColor
|
||||
// Animate shadow color
|
||||
let newShadowColor = isSelected ? Colors.expandedButtonGlowColor.cgColor : UIColor.black.cgColor
|
||||
let shadowAnimation = CABasicAnimation(keyPath: "shadowColor")
|
||||
shadowAnimation.fromValue = layer.shadowColor
|
||||
shadowAnimation.toValue = newShadowColor
|
||||
shadowAnimation.duration = animationDuration
|
||||
layer.add(shadowAnimation, forKey: shadowAnimation.keyPath)
|
||||
layer.shadowColor = newShadowColor
|
||||
UIView.animate(withDuration: 0.3) { [weak self] in
|
||||
self?.themeBorderColor = (self?.isSelected == true ? .primary : .borderSeparator)
|
||||
self?.themeShadowColor = (self?.isSelected == true ? .primary : .black)
|
||||
}
|
||||
|
||||
// Notify delegate
|
||||
if isSelected { delegate.optionViewDidActivate(self) }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Option View Delegate
|
||||
protocol OptionViewDelegate {
|
||||
// MARK: - Option View Delegate
|
||||
|
||||
protocol OptionViewDelegate {
|
||||
func optionViewDidActivate(_ optionView: OptionView)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import UIKit
|
|||
import Sodium
|
||||
import Curve25519Kit
|
||||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
final class RegisterVC : BaseVC {
|
||||
private var seed: Data! { didSet { updateKeyPair() } }
|
||||
|
@ -14,11 +15,11 @@ final class RegisterVC : BaseVC {
|
|||
|
||||
private lazy var publicKeyLabel: UILabel = {
|
||||
let result = UILabel()
|
||||
result.textColor = Colors.text
|
||||
result.font = Fonts.spaceMono(ofSize: isIPhone5OrSmaller ? Values.mediumFontSize : 20)
|
||||
result.numberOfLines = 0
|
||||
result.lineBreakMode = .byCharWrapping
|
||||
result.themeTextColor = .textPrimary
|
||||
result.accessibilityLabel = "Session ID label"
|
||||
result.lineBreakMode = .byCharWrapping
|
||||
result.numberOfLines = 0
|
||||
|
||||
return result
|
||||
}()
|
||||
|
@ -33,21 +34,23 @@ final class RegisterVC : BaseVC {
|
|||
|
||||
private lazy var legalLabel: UILabel = {
|
||||
let result = UILabel()
|
||||
result.textColor = Colors.text
|
||||
result.font = .systemFont(ofSize: Values.verySmallFontSize)
|
||||
result.themeTextColor = .textPrimary
|
||||
let text = "By using this service, you agree to our Terms of Service, End User License Agreement (EULA) and Privacy Policy"
|
||||
let attributedText = NSMutableAttributedString(string: text, attributes: [ .font : UIFont.systemFont(ofSize: Values.verySmallFontSize) ])
|
||||
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "Terms of Service"))
|
||||
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "End User License Agreement (EULA)"))
|
||||
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "Privacy Policy"))
|
||||
result.attributedText = attributedText
|
||||
result.numberOfLines = 0
|
||||
result.textAlignment = .center
|
||||
result.lineBreakMode = .byWordWrapping
|
||||
result.numberOfLines = 0
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: Lifecycle
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
|
@ -57,7 +60,7 @@ final class RegisterVC : BaseVC {
|
|||
let titleLabel = UILabel()
|
||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||
titleLabel.text = "vc_register_title".localized()
|
||||
titleLabel.textColor = Colors.text
|
||||
titleLabel.themeTextColor = .textPrimary
|
||||
titleLabel.lineBreakMode = .byWordWrapping
|
||||
titleLabel.numberOfLines = 0
|
||||
|
||||
|
@ -65,7 +68,7 @@ final class RegisterVC : BaseVC {
|
|||
let explanationLabel = UILabel()
|
||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
explanationLabel.text = "vc_register_explanation".localized()
|
||||
explanationLabel.textColor = Colors.text
|
||||
explanationLabel.themeTextColor = .textPrimary
|
||||
explanationLabel.lineBreakMode = .byWordWrapping
|
||||
explanationLabel.numberOfLines = 0
|
||||
|
||||
|
@ -75,7 +78,7 @@ final class RegisterVC : BaseVC {
|
|||
publicKeyLabel.pin(to: publicKeyLabelContainer, withInset: Values.mediumSpacing)
|
||||
publicKeyLabelContainer.layer.cornerRadius = TextField.cornerRadius
|
||||
publicKeyLabelContainer.layer.borderWidth = 1
|
||||
publicKeyLabelContainer.layer.borderColor = Colors.text.cgColor
|
||||
publicKeyLabelContainer.themeBorderColor = .textPrimary
|
||||
|
||||
// Set up spacers
|
||||
let topSpacer = UIView.vStretchingSpacer()
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import UIKit
|
||||
import SessionUIKit
|
||||
import SessionUtilitiesKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
final class RestoreVC: BaseVC {
|
||||
private var spacer1HeightConstraint: NSLayoutConstraint!
|
||||
|
@ -11,11 +12,12 @@ final class RestoreVC: BaseVC {
|
|||
private var restoreButtonBottomOffsetConstraint: NSLayoutConstraint!
|
||||
private var bottomConstraint: NSLayoutConstraint!
|
||||
|
||||
// MARK: Components
|
||||
// MARK: - Components
|
||||
|
||||
private lazy var mnemonicTextView: TextView = {
|
||||
let result = TextView(placeholder: "vc_restore_seed_text_field_hint".localized())
|
||||
result.autocapitalizationType = .none
|
||||
result.layer.borderColor = Colors.text.cgColor
|
||||
result.themeBorderColor = .textPrimary
|
||||
result.accessibilityLabel = "Recovery phrase text view"
|
||||
|
||||
return result
|
||||
|
@ -34,7 +36,7 @@ final class RestoreVC: BaseVC {
|
|||
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "Terms of Service"))
|
||||
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "End User License Agreement (EULA)"))
|
||||
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "Privacy Policy"))
|
||||
result.textColor = Colors.text
|
||||
result.themeTextColor = .textPrimary
|
||||
result.attributedText = attributedText
|
||||
result.textAlignment = .center
|
||||
result.lineBreakMode = .byWordWrapping
|
||||
|
@ -54,7 +56,7 @@ final class RestoreVC: BaseVC {
|
|||
let titleLabel = UILabel()
|
||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||
titleLabel.text = "vc_restore_title".localized()
|
||||
titleLabel.textColor = Colors.text
|
||||
titleLabel.themeTextColor = .textPrimary
|
||||
titleLabel.lineBreakMode = .byWordWrapping
|
||||
titleLabel.numberOfLines = 0
|
||||
|
||||
|
@ -62,7 +64,7 @@ final class RestoreVC: BaseVC {
|
|||
let explanationLabel = UILabel()
|
||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
explanationLabel.text = "vc_restore_explanation".localized()
|
||||
explanationLabel.textColor = Colors.text
|
||||
explanationLabel.themeTextColor = .textPrimary
|
||||
explanationLabel.lineBreakMode = .byWordWrapping
|
||||
explanationLabel.numberOfLines = 0
|
||||
|
||||
|
@ -70,6 +72,7 @@ final class RestoreVC: BaseVC {
|
|||
legalLabel.isUserInteractionEnabled = true
|
||||
let legalLabelTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleLegalLabelTapped))
|
||||
legalLabel.addGestureRecognizer(legalLabelTapGestureRecognizer)
|
||||
|
||||
// Set up spacers
|
||||
let topSpacer = UIView.vStretchingSpacer()
|
||||
let spacer1 = UIView()
|
||||
|
@ -146,12 +149,15 @@ final class RestoreVC: BaseVC {
|
|||
// MARK: Updating
|
||||
@objc private func handleKeyboardWillChangeFrameNotification(_ notification: Notification) {
|
||||
guard let newHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height else { return }
|
||||
|
||||
bottomConstraint.constant = -newHeight // Negative due to how the constraint is set up
|
||||
restoreButtonBottomOffsetConstraint.constant = isIPhone6OrSmaller ? Values.smallSpacing : Values.largeSpacing
|
||||
spacer1HeightConstraint.constant = isIPhone6OrSmaller ? Values.smallSpacing : Values.mediumSpacing
|
||||
spacer2HeightConstraint.constant = isIPhone6OrSmaller ? Values.smallSpacing : Values.mediumSpacing
|
||||
spacer3HeightConstraint.constant = isIPhone6OrSmaller ? Values.smallSpacing : Values.mediumSpacing
|
||||
|
||||
if isIPhone5OrSmaller { legalLabel.isUserInteractionEnabled = false }
|
||||
|
||||
UIView.animate(withDuration: 0.25) {
|
||||
if isIPhone5OrSmaller { self.legalLabel.alpha = 0 }
|
||||
self.view.layoutIfNeeded()
|
||||
|
@ -164,20 +170,30 @@ final class RestoreVC: BaseVC {
|
|||
spacer1HeightConstraint.constant = isIPhone5OrSmaller ? Values.smallSpacing : Values.veryLargeSpacing
|
||||
spacer2HeightConstraint.constant = isIPhone5OrSmaller ? Values.smallSpacing : Values.veryLargeSpacing
|
||||
spacer3HeightConstraint.constant = isIPhone5OrSmaller ? Values.smallSpacing : Values.veryLargeSpacing
|
||||
|
||||
if isIPhone5OrSmaller { legalLabel.isUserInteractionEnabled = true }
|
||||
|
||||
UIView.animate(withDuration: 0.25) {
|
||||
if isIPhone5OrSmaller { self.legalLabel.alpha = 1 }
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Interaction
|
||||
// MARK: - Interaction
|
||||
|
||||
@objc private func restore() {
|
||||
func showError(title: String, message: String = "") {
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
|
||||
presentAlert(alert)
|
||||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: title,
|
||||
explanation: message,
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .textPrimary
|
||||
)
|
||||
)
|
||||
present(modal, animated: true)
|
||||
}
|
||||
|
||||
let mnemonic = mnemonicTextView.text!.lowercased()
|
||||
do {
|
||||
let hexEncodedSeed = try Mnemonic.decode(mnemonic: mnemonic)
|
||||
|
@ -185,6 +201,7 @@ final class RestoreVC: BaseVC {
|
|||
let (ed25519KeyPair, x25519KeyPair) = try! Identity.generate(from: seed)
|
||||
Onboarding.Flow.recover.preregister(with: seed, ed25519KeyPair: ed25519KeyPair, x25519KeyPair: x25519KeyPair)
|
||||
mnemonicTextView.resignFirstResponder()
|
||||
|
||||
Timer.scheduledTimer(withTimeInterval: 0.25, repeats: false) { _ in
|
||||
let displayNameVC = DisplayNameVC()
|
||||
self.navigationController!.pushViewController(displayNameVC, animated: true)
|
||||
|
@ -202,6 +219,7 @@ final class RestoreVC: BaseVC {
|
|||
let ppRange = (legalLabel.text! as NSString).range(of: "Privacy Policy")
|
||||
let touchInLegalLabelCoordinates = tapGestureRecognizer.location(in: legalLabel)
|
||||
let characterIndex = legalLabel.characterIndex(for: touchInLegalLabelCoordinates)
|
||||
|
||||
if tosRange.contains(characterIndex) {
|
||||
urlAsString = "https://getsession.org/terms-of-service/"
|
||||
} else if eulaRange.contains(characterIndex) {
|
||||
|
@ -211,6 +229,7 @@ final class RestoreVC: BaseVC {
|
|||
} else {
|
||||
urlAsString = nil
|
||||
}
|
||||
|
||||
if let urlAsString = urlAsString {
|
||||
let url = URL(string: urlAsString)!
|
||||
UIApplication.shared.open(url)
|
||||
|
|
|
@ -38,8 +38,8 @@ final class SeedReminderView: UIView {
|
|||
let result = UILabel()
|
||||
result.font = .systemFont(ofSize: Values.verySmallFontSize)
|
||||
result.themeTextColor = .textSecondary
|
||||
result.numberOfLines = 0
|
||||
result.lineBreakMode = .byWordWrapping
|
||||
result.numberOfLines = 2
|
||||
|
||||
return result
|
||||
}()
|
||||
|
@ -66,8 +66,17 @@ final class SeedReminderView: UIView {
|
|||
// Set background color
|
||||
themeBackgroundColor = .conversationButton_background
|
||||
|
||||
// Note: We hard-code the height of the subtitle to 2 lines so changing it's content
|
||||
// doesn't result in the view changing height (which looks buggy)
|
||||
let subtitleContainerView: UIView = UIView()
|
||||
subtitleContainerView.set(.height, to: (subtitleLabel.font.lineHeight * 2))
|
||||
subtitleContainerView.addSubview(subtitleLabel)
|
||||
subtitleLabel.pin(.top, to: .top, of: subtitleContainerView)
|
||||
subtitleLabel.pin(.leading, to: .leading, of: subtitleContainerView)
|
||||
subtitleLabel.pin(.trailing, to: .trailing, of: subtitleContainerView)
|
||||
|
||||
// Set up label stack view
|
||||
let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, subtitleLabel ])
|
||||
let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, subtitleContainerView ])
|
||||
labelStackView.axis = .vertical
|
||||
labelStackView.spacing = 4
|
||||
|
||||
|
@ -103,12 +112,12 @@ final class SeedReminderView: UIView {
|
|||
stackView.pin(to: self)
|
||||
}
|
||||
|
||||
// MARK: Updating
|
||||
// MARK: - Updating
|
||||
|
||||
func setProgress(_ progress: Float, animated isAnimated: Bool) {
|
||||
progressIndicatorView.setProgress(progress, animated: isAnimated)
|
||||
}
|
||||
|
||||
// MARK: Updating
|
||||
@objc private func handleContinueButtonTapped() {
|
||||
delegate?.handleContinueButtonTapped(from: self)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import UIKit
|
||||
import SessionUIKit
|
||||
import SessionUtilitiesKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
final class SeedVC: BaseVC {
|
||||
private let mnemonic: String = {
|
||||
|
@ -26,20 +27,27 @@ final class SeedVC: BaseVC {
|
|||
|
||||
private lazy var seedReminderView: SeedReminderView = {
|
||||
let result = SeedReminderView(hasContinueButton: false)
|
||||
let title = "You're almost finished! 90%"
|
||||
let attributedTitle = NSMutableAttributedString(string: title)
|
||||
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "90%"))
|
||||
result.title = attributedTitle
|
||||
result.subtitle = "view_seed_reminder_subtitle_2".localized()
|
||||
result.setProgress(0.9, animated: false)
|
||||
|
||||
ThemeManager.onThemeChange(observer: result) { [weak result] _, primaryColor in
|
||||
let title = "You're almost finished! 90%"
|
||||
let attributedTitle = NSMutableAttributedString(string: title)
|
||||
attributedTitle.addAttribute(
|
||||
.foregroundColor,
|
||||
value: primaryColor.color,
|
||||
range: (title as NSString).range(of: "90%")
|
||||
)
|
||||
result?.title = attributedTitle
|
||||
}
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var mnemonicLabel: UILabel = {
|
||||
let result = UILabel()
|
||||
result.font = Fonts.spaceMono(ofSize: Values.mediumFontSize)
|
||||
result.textColor = Colors.accent
|
||||
result.themeTextColor = .primary
|
||||
result.textAlignment = .center
|
||||
result.lineBreakMode = .byWordWrapping
|
||||
result.numberOfLines = 0
|
||||
|
@ -64,14 +72,14 @@ final class SeedVC: BaseVC {
|
|||
|
||||
// Set up navigation bar buttons
|
||||
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
|
||||
closeButton.tintColor = Colors.text
|
||||
closeButton.themeTintColor = .textPrimary
|
||||
navigationItem.leftBarButtonItem = closeButton
|
||||
|
||||
// Set up title label
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||
titleLabel.text = "vc_seed_title_2".localized()
|
||||
titleLabel.textColor = Colors.text
|
||||
titleLabel.themeTextColor = .textPrimary
|
||||
titleLabel.lineBreakMode = .byWordWrapping
|
||||
titleLabel.numberOfLines = 0
|
||||
|
||||
|
@ -79,7 +87,7 @@ final class SeedVC: BaseVC {
|
|||
let explanationLabel = UILabel()
|
||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
explanationLabel.text = "vc_seed_explanation".localized()
|
||||
explanationLabel.textColor = Colors.text
|
||||
explanationLabel.themeTextColor = .textPrimary
|
||||
explanationLabel.lineBreakMode = .byWordWrapping
|
||||
explanationLabel.numberOfLines = 0
|
||||
|
||||
|
@ -94,15 +102,15 @@ final class SeedVC: BaseVC {
|
|||
let mnemonicLabelContainer = UIView()
|
||||
mnemonicLabelContainer.addSubview(mnemonicLabel)
|
||||
mnemonicLabel.pin(to: mnemonicLabelContainer, withInset: isIPhone6OrSmaller ? Values.smallSpacing : Values.mediumSpacing)
|
||||
mnemonicLabelContainer.themeBorderColor = .textPrimary
|
||||
mnemonicLabelContainer.layer.cornerRadius = TextField.cornerRadius
|
||||
mnemonicLabelContainer.layer.borderWidth = 1
|
||||
mnemonicLabelContainer.layer.borderColor = Colors.text.cgColor
|
||||
|
||||
// Set up call to action label
|
||||
let callToActionLabel = UILabel()
|
||||
callToActionLabel.font = .systemFont(ofSize: isIPhone5OrSmaller ? Values.smallFontSize : Values.mediumFontSize)
|
||||
callToActionLabel.text = "vc_seed_reveal_button_title".localized()
|
||||
callToActionLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
|
||||
callToActionLabel.themeTextColor = .textSecondary
|
||||
callToActionLabel.textAlignment = .center
|
||||
|
||||
let callToActionLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic))
|
||||
|
@ -179,14 +187,20 @@ final class SeedVC: BaseVC {
|
|||
@objc private func revealMnemonic() {
|
||||
UIView.transition(with: mnemonicLabel, duration: 0.25, options: .transitionCrossDissolve, animations: {
|
||||
self.mnemonicLabel.text = self.mnemonic
|
||||
self.mnemonicLabel.textColor = Colors.text
|
||||
self.mnemonicLabel.themeTextColor = .textPrimary
|
||||
}, completion: nil)
|
||||
|
||||
UIView.transition(with: seedReminderView.titleLabel, duration: 0.25, options: .transitionCrossDissolve, animations: {
|
||||
let title = "Account Secured! 100%"
|
||||
let attributedTitle = NSMutableAttributedString(string: title)
|
||||
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "100%"))
|
||||
self.seedReminderView.title = attributedTitle
|
||||
ThemeManager.onThemeChange(observer: self.seedReminderView) { [weak self] _, primaryColor in
|
||||
let title = "Account Secured! 100%"
|
||||
let attributedTitle = NSMutableAttributedString(string: title)
|
||||
attributedTitle.addAttribute(
|
||||
.foregroundColor,
|
||||
value: primaryColor.color,
|
||||
range: (title as NSString).range(of: "100%")
|
||||
)
|
||||
self?.seedReminderView.title = attributedTitle
|
||||
}
|
||||
}, completion: nil)
|
||||
|
||||
UIView.transition(with: seedReminderView.subtitleLabel, duration: 1, options: .transitionCrossDissolve, animations: {
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
import GRDB
|
||||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class JoinOpenGroupVC: BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, OWSQRScannerDelegate {
|
||||
final class JoinOpenGroupVC: BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, QRScannerDelegate {
|
||||
private let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
|
||||
private var pages: [UIViewController] = []
|
||||
private var isJoining = false
|
||||
|
@ -136,7 +137,7 @@ final class JoinOpenGroupVC: BaseVC, UIPageViewControllerDataSource, UIPageViewC
|
|||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func controller(_ controller: OWSQRCodeScanningViewController, didDetectQRCodeWith string: String) {
|
||||
func controller(_ controller: QRCodeScanningViewController, didDetectQRCodeWith string: String) {
|
||||
joinOpenGroup(with: string)
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class BlockedContactsViewController: BaseVC, UITableViewDelegate, UITableViewDat
|
|||
private lazy var tableView: UITableView = {
|
||||
let result: UITableView = UITableView()
|
||||
result.translatesAutoresizingMaskIntoConstraints = false
|
||||
result.backgroundColor = .clear
|
||||
result.themeBackgroundColor = .clear
|
||||
result.separatorStyle = .none
|
||||
result.register(view: BlockedContactCell.self)
|
||||
result.dataSource = self
|
||||
|
@ -288,7 +288,7 @@ class BlockedContactsViewController: BaseVC, UITableViewDelegate, UITableViewDat
|
|||
switch section.model {
|
||||
case .loadMore:
|
||||
let loadingIndicator: UIActivityIndicatorView = UIActivityIndicatorView(style: .medium)
|
||||
loadingIndicator.tintColor = Colors.text
|
||||
loadingIndicator.themeTintColor = .textPrimary
|
||||
loadingIndicator.alpha = 0.5
|
||||
loadingIndicator.startAnimating()
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
import Curve25519Kit
|
||||
import SessionUIKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, OWSQRScannerDelegate {
|
||||
final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, QRScannerDelegate {
|
||||
private let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
|
||||
private var pages: [UIViewController] = []
|
||||
private var targetVCIndex: Int?
|
||||
|
@ -127,7 +128,7 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
|
|||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func controller(_ controller: OWSQRCodeScanningViewController, didDetectQRCodeWith string: String) {
|
||||
func controller(_ controller: QRCodeScanningViewController, didDetectQRCodeWith string: String) {
|
||||
let hexEncodedPublicKey = string
|
||||
startNewPrivateChatIfPossible(with: hexEncodedPublicKey)
|
||||
}
|
||||
|
@ -200,12 +201,12 @@ private final class ViewMyQRCodeVC : UIViewController {
|
|||
ThemeManager.onThemeChange(observer: qrCodeImageView) { theme, _ in
|
||||
switch theme.interfaceStyle {
|
||||
case .light:
|
||||
qrCodeImageView.tintColor = theme.colors[.textPrimary]
|
||||
qrCodeImageViewBackgroundView.backgroundColor = nil
|
||||
qrCodeImageView.themeTintColorForced = .theme(theme, color: .textPrimary)
|
||||
qrCodeImageViewBackgroundView.themeBackgroundColorForced = nil
|
||||
|
||||
default:
|
||||
qrCodeImageView.tintColor = theme.colors[.backgroundPrimary]
|
||||
qrCodeImageViewBackgroundView.backgroundColor = .white
|
||||
qrCodeImageView.themeTintColorForced = .theme(theme, color: .backgroundPrimary)
|
||||
qrCodeImageViewBackgroundView.themeBackgroundColorForced = .color(.white)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class SettingsTableViewController<NavItemId: Equatable, Section: SettingSection,
|
|||
let result: UITableView = UITableView()
|
||||
result.translatesAutoresizingMaskIntoConstraints = false
|
||||
result.separatorStyle = .none
|
||||
result.backgroundColor = .clear
|
||||
result.themeBackgroundColor = .clear
|
||||
result.showsVerticalScrollIndicator = false
|
||||
result.showsHorizontalScrollIndicator = false
|
||||
result.register(view: SettingsAvatarCell.self)
|
||||
|
|
|
@ -60,7 +60,7 @@ class PrimaryColorSelectionView: UIView {
|
|||
|
||||
private func setupUI(color: Theme.PrimaryColor) {
|
||||
// Set the appropriate colours
|
||||
selectionView.backgroundColor = color.color
|
||||
selectionView.themeBackgroundColorForced = .primary(color)
|
||||
|
||||
// Add the UI
|
||||
addSubview(backgroundButton)
|
||||
|
|
|
@ -80,10 +80,10 @@ class ThemeSelectionView: UIView {
|
|||
self.themeBackgroundColor = .appearance_sectionBackground
|
||||
|
||||
// Set the appropriate colours
|
||||
previewView.backgroundColor = theme.colors[.backgroundPrimary]
|
||||
previewView.layer.borderColor = theme.colors[.borderSeparator]?.cgColor
|
||||
previewIncomingMessageView.backgroundColor = theme.colors[.messageBubble_incomingBackground]
|
||||
previewOutgoingMessageView.backgroundColor = theme.colors[.defaultPrimary]
|
||||
previewView.themeBackgroundColorForced = .theme(theme, color: .backgroundPrimary)
|
||||
previewView.themeBorderColorForced = .theme(theme, color: .borderSeparator)
|
||||
previewIncomingMessageView.themeBackgroundColorForced = .theme(theme, color: .messageBubble_incomingBackground)
|
||||
previewOutgoingMessageView.themeBackgroundColorForced = .theme(theme, color: .defaultPrimary)
|
||||
selectionView.text = theme.title
|
||||
|
||||
// Add the UI
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
|
||||
public protocol CaptionContainerViewDelegate: AnyObject {
|
||||
func captionContainerViewDidUpdateText(_ captionContainerView: CaptionContainerView)
|
||||
|
@ -100,8 +101,8 @@ private class CaptionView: UIView {
|
|||
let textView = CaptionTextView()
|
||||
|
||||
textView.font = UIFont.ows_dynamicTypeBody
|
||||
textView.textColor = Colors.text
|
||||
textView.backgroundColor = .clear
|
||||
textView.themeTextColor = .textPrimary
|
||||
textView.themeBackgroundColor = .clear
|
||||
textView.isEditable = false
|
||||
textView.isSelectable = false
|
||||
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol DismissInputBarDelegate: class {
|
||||
func dismissInputBarDidTapDismiss(_ dismissInputBar: DismissInputBar)
|
||||
}
|
||||
|
||||
class DismissInputBar: UIToolbar {
|
||||
|
||||
weak var dismissDelegate: DismissInputBarDelegate?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
||||
let dismissButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(didTapDone))
|
||||
dismissButton.imageInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 40)
|
||||
dismissButton.tintColor = UIColor.ows_systemPrimaryButton
|
||||
|
||||
self.items = [spacer, dismissButton]
|
||||
self.isTranslucent = false
|
||||
self.isOpaque = true
|
||||
self.barTintColor = UIColor.lokiDarkestGray()
|
||||
|
||||
self.autoresizingMask = .flexibleHeight
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
@objc
|
||||
public func didTapDone() {
|
||||
self.dismissDelegate?.dismissInputBarDidTapDismiss(self)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public class DismissableTextField: UITextField, DismissInputBarDelegate {
|
||||
|
||||
private let dismissBar: DismissInputBar
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.dismissBar = DismissInputBar()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.inputAccessoryView = dismissBar
|
||||
|
||||
dismissBar.dismissDelegate = self
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
// MARK: DismissInputBarDelegate
|
||||
|
||||
func dismissInputBarDidTapDismiss(_ dismissInputBar: DismissInputBar) {
|
||||
self.resignFirstResponder()
|
||||
}
|
||||
}
|
|
@ -407,7 +407,7 @@ public final class FullConversationCell: UITableViewCell {
|
|||
hasAtLeastOneReadReceipt: (cellViewModel.interactionHasAtLeastOneReadReceipt ?? false)
|
||||
)
|
||||
statusIndicatorView.image = stateInfo?.image
|
||||
statusIndicatorView.themeTintColor = stateInfo?.tintColor
|
||||
statusIndicatorView.themeTintColor = stateInfo?.themeTintColor
|
||||
statusIndicatorView.isHidden = (
|
||||
cellViewModel.interactionVariant != .standardOutgoing &&
|
||||
cellViewModel.interactionState != .skipped
|
||||
|
@ -456,7 +456,7 @@ public final class FullConversationCell: UITableViewCell {
|
|||
}
|
||||
else if cellViewModel.threadOnlyNotifyForMentions == true {
|
||||
let imageAttachment = NSTextAttachment()
|
||||
imageAttachment.image = UIImage(named: "NotifyMentions.png")?.asTintedImage(color: textColor)
|
||||
imageAttachment.image = UIImage(named: "NotifyMentions.png")?.withTint(textColor)
|
||||
imageAttachment.bounds = CGRect(x: 0, y: -2, width: Values.smallFontSize, height: Values.smallFontSize)
|
||||
|
||||
let imageString = NSAttributedString(attachment: imageAttachment)
|
||||
|
|
|
@ -25,7 +25,7 @@ public class LoadingViewController: UIViewController {
|
|||
private var logoView: UIImageView = {
|
||||
let result: UIImageView = UIImageView(image: #imageLiteral(resourceName: "SessionGreen64"))
|
||||
result.contentMode = .scaleAspectFit
|
||||
result.layer.shadowColor = Theme.PrimaryColor.green.color.cgColor
|
||||
result.themeShadowColorForced = .primary(.green)
|
||||
result.layer.shadowOffset = .zero
|
||||
result.layer.shadowRadius = 3
|
||||
result.layer.shadowOpacity = 0
|
||||
|
@ -37,8 +37,8 @@ public class LoadingViewController: UIViewController {
|
|||
let result: UIProgressView = UIProgressView(progressViewStyle: .bar)
|
||||
result.clipsToBounds = true
|
||||
result.progress = 0
|
||||
result.tintColor = Theme.PrimaryColor.green.color
|
||||
result.progressTintColor = Theme.classicDark.colors[.textPrimary]?.withAlphaComponent(0.1)
|
||||
result.themeTintColorForced = .primary(.green)
|
||||
result.themeProgressTintColorForced = .theme(.classicDark, color: .textPrimary, alpha: 0.1)
|
||||
result.layer.cornerRadius = 6
|
||||
|
||||
return result
|
||||
|
@ -48,7 +48,7 @@ public class LoadingViewController: UIViewController {
|
|||
let result: UILabel = UILabel()
|
||||
result.font = UIFont.systemFont(ofSize: Values.mediumFontSize)
|
||||
result.text = "DATABASE_VIEW_OVERLAY_TITLE".localized()
|
||||
result.textColor = Theme.classicDark.colors[.textPrimary]
|
||||
result.themeTextColorForced = .theme(.classicDark, color: .textPrimary)
|
||||
result.textAlignment = .center
|
||||
result.numberOfLines = 0
|
||||
result.lineBreakMode = .byWordWrapping
|
||||
|
@ -60,7 +60,7 @@ public class LoadingViewController: UIViewController {
|
|||
let result: UILabel = UILabel()
|
||||
result.font = UIFont.systemFont(ofSize: Values.verySmallFontSize)
|
||||
result.text = "DATABASE_VIEW_OVERLAY_SUBTITLE".localized()
|
||||
result.textColor = Theme.classicDark.colors[.textPrimary]
|
||||
result.themeTextColorForced = .theme(.classicDark, color: .textPrimary)
|
||||
result.textAlignment = .center
|
||||
result.numberOfLines = 0
|
||||
result.lineBreakMode = .byWordWrapping
|
||||
|
@ -84,7 +84,7 @@ public class LoadingViewController: UIViewController {
|
|||
override public func loadView() {
|
||||
self.view = UIView()
|
||||
|
||||
self.view.backgroundColor = Theme.classicDark.colors[.backgroundPrimary]
|
||||
self.view.themeBackgroundColorForced = .theme(.classicDark, color: .backgroundPrimary)
|
||||
|
||||
self.view.addSubview(self.logoView)
|
||||
self.view.addSubview(self.labelStack)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,18 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
// This view can be used to safely fill a region of a table
|
||||
// or collection view cell. These cells change the background
|
||||
// colors of their subviews when selected. This can inadvertently
|
||||
// change the color of filled subviews. This view will
|
||||
// reject a new background once its background has been set.
|
||||
@objc class NeverClearView: UIView {
|
||||
override var backgroundColor: UIColor? {
|
||||
didSet {
|
||||
if backgroundColor?.cgColor.alpha == 0 {
|
||||
backgroundColor = oldValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,7 +33,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
{
|
||||
self.opaque = NO;
|
||||
self.userInteractionEnabled = NO;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSProgressView : UIView
|
||||
|
||||
@property (nonatomic) UIColor *color;
|
||||
@property (nonatomic) CGFloat progress;
|
||||
|
||||
+ (CGSize)defaultSize;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,132 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSProgressView.h"
|
||||
#import <SessionUtilitiesKit/UIView+OWS.h>
|
||||
#import <SessionUtilitiesKit/OWSMath.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSProgressView ()
|
||||
|
||||
@property (nonatomic) CAShapeLayer *borderLayer;
|
||||
@property (nonatomic) CAShapeLayer *progressLayer;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation OWSProgressView
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self initCommon];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self initCommon];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initCommon
|
||||
{
|
||||
self.opaque = NO;
|
||||
self.userInteractionEnabled = NO;
|
||||
self.color = [UIColor whiteColor];
|
||||
|
||||
// Prevent the shape layer from animating changes.
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
|
||||
self.borderLayer = [CAShapeLayer new];
|
||||
[self.layer addSublayer:self.borderLayer];
|
||||
|
||||
self.progressLayer = [CAShapeLayer new];
|
||||
[self.layer addSublayer:self.progressLayer];
|
||||
|
||||
[CATransaction commit];
|
||||
|
||||
[self setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
|
||||
[self setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)setProgress:(CGFloat)progress
|
||||
{
|
||||
if (_progress != progress) {
|
||||
_progress = progress;
|
||||
[self update];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setColor:(UIColor *)color
|
||||
{
|
||||
if (![_color isEqual:color]) {
|
||||
_color = color;
|
||||
[self update];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)update
|
||||
{
|
||||
// Prevent the shape layer from animating changes.
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
|
||||
CGFloat borderThickness = MAX(CGHairlineWidth(), self.bounds.size.height * 0.1f);
|
||||
CGFloat cornerRadius = MIN(self.bounds.size.width, self.bounds.size.height) * 0.5f;
|
||||
|
||||
// Add the outer border.
|
||||
UIBezierPath *borderPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:cornerRadius];
|
||||
self.borderLayer.path = borderPath.CGPath;
|
||||
self.borderLayer.strokeColor = self.color.CGColor;
|
||||
self.borderLayer.lineWidth = borderThickness;
|
||||
self.borderLayer.fillColor = [UIColor clearColor].CGColor;
|
||||
|
||||
// Add the inner progress.
|
||||
CGRect progressRect = self.bounds;
|
||||
progressRect.size.width = cornerRadius * 2;
|
||||
CGFloat baseProgress = borderThickness * 2;
|
||||
CGFloat minProgress = baseProgress;
|
||||
CGFloat maxProgress = MAX(0, self.bounds.size.width - baseProgress);
|
||||
progressRect.size.width = CGFloatLerp(minProgress, maxProgress, CGFloatClamp01(self.progress));
|
||||
UIBezierPath *progressPath = [UIBezierPath bezierPathWithRoundedRect:progressRect cornerRadius:cornerRadius];
|
||||
self.progressLayer.path = progressPath.CGPath;
|
||||
self.progressLayer.fillColor = self.color.CGColor;
|
||||
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
+ (CGSize)defaultSize
|
||||
{
|
||||
return CGSizeMake(150, 16);
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size
|
||||
{
|
||||
return OWSProgressView.defaultSize;
|
||||
}
|
||||
|
||||
- (CGSize)intrinsicContentSize
|
||||
{
|
||||
return CGSizeMake(UIViewNoIntrinsicMetric, 16);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,34 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <SignalUtilitiesKit/OWSViewController.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <ZXingObjC/ZXingObjC.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSQRCodeScanningViewController;
|
||||
|
||||
@protocol OWSQRScannerDelegate
|
||||
|
||||
@optional
|
||||
|
||||
- (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithString:(NSString *)string;
|
||||
- (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithData:(NSData *)data;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface OWSQRCodeScanningViewController
|
||||
: OWSViewController <AVCaptureMetadataOutputObjectsDelegate, ZXCaptureDelegate>
|
||||
|
||||
@property (nonatomic, weak) UIViewController<OWSQRScannerDelegate> *scanDelegate;
|
||||
|
||||
- (void)startCapture;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,162 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSQRCodeScanningViewController.h"
|
||||
#import "OWSBezierPathView.h"
|
||||
#import "UIColor+OWS.h"
|
||||
#import "UIView+OWS.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSQRCodeScanningViewController ()
|
||||
|
||||
@property (atomic) ZXCapture *capture;
|
||||
@property (nonatomic) BOOL captureEnabled;
|
||||
@property (nonatomic) UIView *maskingView;
|
||||
@property (nonatomic) dispatch_queue_t captureQueue;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation OWSQRCodeScanningViewController
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self.capture.layer removeFromSuperlayer];
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_captureEnabled = NO;
|
||||
_captureQueue = dispatch_get_main_queue();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_captureEnabled = NO;
|
||||
_captureQueue = dispatch_get_main_queue();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
[super loadView];
|
||||
|
||||
OWSBezierPathView *maskingView = [OWSBezierPathView new];
|
||||
self.maskingView = maskingView;
|
||||
[maskingView setConfigureShapeLayerBlock:^(CAShapeLayer *layer, CGRect bounds) {
|
||||
// Add a circular mask
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRect:bounds];
|
||||
CGFloat margin = ScaleFromIPhone5To7Plus(24.f, 48.f);
|
||||
CGFloat radius = MIN(bounds.size.width, bounds.size.height) * 0.5f - margin;
|
||||
|
||||
// Center the circle's bounding rectangle
|
||||
CGRect circleRect = CGRectMake(
|
||||
bounds.size.width * 0.5f - radius, bounds.size.height * 0.5f - radius, radius * 2.f, radius * 2.f);
|
||||
UIBezierPath *circlePath = [UIBezierPath bezierPathWithRoundedRect:circleRect cornerRadius:16.f];
|
||||
[path appendPath:circlePath];
|
||||
[path setUsesEvenOddFillRule:YES];
|
||||
|
||||
layer.path = path.CGPath;
|
||||
layer.fillRule = kCAFillRuleEvenOdd;
|
||||
layer.fillColor = UIColor.lokiDarkestGray.CGColor;
|
||||
layer.opacity = 0.32f;
|
||||
}];
|
||||
[self.view addSubview:maskingView];
|
||||
[maskingView ows_autoPinToSuperviewEdges];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (self.captureEnabled) {
|
||||
[self startCapture];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
[self stopCapture];
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
self.capture.layer.frame = self.view.bounds;
|
||||
}
|
||||
|
||||
- (void)startCapture
|
||||
{
|
||||
self.captureEnabled = YES;
|
||||
if (!self.capture) {
|
||||
dispatch_async(self.captureQueue, ^{
|
||||
self.capture = [[ZXCapture alloc] init];
|
||||
self.capture.camera = self.capture.back;
|
||||
self.capture.focusMode = AVCaptureFocusModeContinuousAutoFocus;
|
||||
self.capture.delegate = self;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.capture.layer.frame = self.view.bounds;
|
||||
[self.view.layer addSublayer:self.capture.layer];
|
||||
[self.view bringSubviewToFront:self.maskingView];
|
||||
[self.capture start];
|
||||
});
|
||||
});
|
||||
} else {
|
||||
[self.capture start];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopCapture
|
||||
{
|
||||
self.captureEnabled = NO;
|
||||
dispatch_async(self.captureQueue, ^{
|
||||
[self.capture stop];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)captureResult:(ZXCapture *)capture result:(ZXResult *)result
|
||||
{
|
||||
if (!self.captureEnabled) {
|
||||
return;
|
||||
}
|
||||
[self stopCapture];
|
||||
|
||||
// Vibrate
|
||||
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
|
||||
|
||||
if (self.scanDelegate) {
|
||||
if ([self.scanDelegate respondsToSelector:@selector(controller:didDetectQRCodeWithData:)]) {
|
||||
OWSLogInfo(@"Scanned Data Code.");
|
||||
ZXByteArray *byteArray = result.resultMetadata[@(kResultMetadataTypeByteSegments)][0];
|
||||
NSData *decodedData = [NSData dataWithBytes:byteArray.array length:byteArray.length];
|
||||
|
||||
[self.scanDelegate controller:self didDetectQRCodeWithData:decodedData];
|
||||
}
|
||||
|
||||
if ([self.scanDelegate respondsToSelector:@selector(controller:didDetectQRCodeWithString:)]) {
|
||||
OWSLogInfo(@"Scanned String Code.");
|
||||
[self.scanDelegate controller:self didDetectQRCodeWithString:result.text];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,150 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
import ZXingObjC
|
||||
import SessionUIKit
|
||||
|
||||
protocol QRScannerDelegate: AnyObject {
|
||||
func controller(_ controller: QRCodeScanningViewController, didDetectQRCodeWith string: String)
|
||||
}
|
||||
|
||||
class QRCodeScanningViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate, ZXCaptureDelegate {
|
||||
public weak var scanDelegate: QRScannerDelegate?
|
||||
|
||||
private let captureQueue: DispatchQueue = DispatchQueue.global(qos: .default)
|
||||
private var capture: ZXCapture?
|
||||
private var captureEnabled: Bool = false
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
deinit {
|
||||
self.capture?.layer.removeFromSuperlayer()
|
||||
}
|
||||
|
||||
// MARK: - Components
|
||||
|
||||
private let maskingView: UIView = {
|
||||
let result: OWSBezierPathView = OWSBezierPathView()
|
||||
result.configureShapeLayerBlock = { layer, bounds in
|
||||
// Add a circular mask
|
||||
let path: UIBezierPath = UIBezierPath(rect: bounds)
|
||||
let margin: CGFloat = ScaleFromIPhone5To7Plus(24, 48)
|
||||
let radius: CGFloat = ((min(bounds.size.width, bounds.size.height) * 0.5) - margin)
|
||||
|
||||
// Center the circle's bounding rectangle
|
||||
let circleRect: CGRect = CGRect(
|
||||
x: ((bounds.size.width * 0.5) - radius),
|
||||
y: ((bounds.size.height * 0.5) - radius),
|
||||
width: (radius * 2),
|
||||
height: (radius * 2)
|
||||
)
|
||||
let circlePath: UIBezierPath = UIBezierPath.init(
|
||||
roundedRect: circleRect,
|
||||
cornerRadius: 16
|
||||
)
|
||||
path.append(circlePath)
|
||||
path.usesEvenOddFillRule = true
|
||||
|
||||
layer.path = path.cgPath
|
||||
layer.fillRule = .evenOdd
|
||||
layer.themeFillColor = .black
|
||||
layer.opacity = 0.32
|
||||
}
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override func loadView() {
|
||||
super.loadView()
|
||||
|
||||
self.view.addSubview(maskingView)
|
||||
maskingView.pin(to: self.view)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if captureEnabled {
|
||||
self.startCapture()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
self.stopCapture()
|
||||
}
|
||||
|
||||
override func viewWillLayoutSubviews() {
|
||||
super.viewWillLayoutSubviews()
|
||||
|
||||
// Note: When accessing 'capture.layer' if the setup hasn't been completed it
|
||||
// will result in a layout being triggered which creates an infinite loop, this
|
||||
// check prevents that case
|
||||
if let capture: ZXCapture = self.capture {
|
||||
capture.layer.frame = self.view.bounds
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
public func startCapture() {
|
||||
self.captureEnabled = true
|
||||
|
||||
// Note: The simulator doesn't support video but if we do try to start an
|
||||
// AVCaptureSession it seems to hang on that particular thread indefinitely
|
||||
// this will prevent us from trying to start a session on the simulator
|
||||
#if targetEnvironment(simulator)
|
||||
#else
|
||||
if self.capture == nil {
|
||||
self.captureQueue.async { [weak self] in
|
||||
let capture: ZXCapture = ZXCapture()
|
||||
capture.camera = capture.back()
|
||||
capture.focusMode = .autoFocus
|
||||
capture.delegate = self
|
||||
capture.start()
|
||||
|
||||
// Note: When accessing the 'layer' for the first time it will create
|
||||
// an instance of 'AVCaptureVideoPreviewLayer', this can hang a little
|
||||
// so we do this on the background thread first
|
||||
if capture.layer != nil {}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
capture.layer.frame = (self?.view.bounds ?? .zero)
|
||||
self?.view.layer.addSublayer(capture.layer)
|
||||
|
||||
if let maskingView: UIView = self?.maskingView {
|
||||
self?.view.bringSubviewToFront(maskingView)
|
||||
}
|
||||
|
||||
self?.capture = capture
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.capture?.start()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private func stopCapture() {
|
||||
self.captureEnabled = false
|
||||
self.captureQueue.async { [weak self] in
|
||||
self?.capture?.stop()
|
||||
}
|
||||
}
|
||||
|
||||
internal func captureResult(_ capture: ZXCapture, result: ZXResult) {
|
||||
guard self.captureEnabled else { return }
|
||||
|
||||
self.stopCapture()
|
||||
|
||||
// Vibrate
|
||||
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
|
||||
|
||||
self.scanDelegate?.controller(self, didDetectQRCodeWith: result.text)
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SessionUIKit
|
||||
|
||||
class ReminderView: UIView {
|
||||
|
||||
let label = UILabel()
|
||||
|
||||
typealias Action = () -> Void
|
||||
|
||||
var tapAction: Action?
|
||||
|
||||
var text: String? {
|
||||
get {
|
||||
return label.text
|
||||
}
|
||||
|
||||
set(newText) {
|
||||
label.text = newText
|
||||
}
|
||||
}
|
||||
|
||||
enum ReminderViewMode {
|
||||
// Nags are urgent interactive prompts, bidding for the user's attention.
|
||||
case nag
|
||||
// Explanations are not interactive or urgent.
|
||||
case explanation
|
||||
}
|
||||
let mode: ReminderViewMode
|
||||
|
||||
@available(*, unavailable, message:"use other constructor instead.")
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
@available(*, unavailable, message:"use other constructor instead.")
|
||||
override init(frame: CGRect) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
private init(mode: ReminderViewMode,
|
||||
text: String, tapAction: Action?) {
|
||||
self.mode = mode
|
||||
self.tapAction = tapAction
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
self.text = text
|
||||
|
||||
setupSubviews()
|
||||
}
|
||||
|
||||
@objc public class func nag(text: String, tapAction: Action?) -> ReminderView {
|
||||
return ReminderView(mode: .nag, text: text, tapAction: tapAction)
|
||||
}
|
||||
|
||||
@objc public class func explanation(text: String) -> ReminderView {
|
||||
return ReminderView(mode: .explanation, text: text, tapAction: nil)
|
||||
}
|
||||
|
||||
func setupSubviews() {
|
||||
let textColor: UIColor
|
||||
let iconColor: UIColor
|
||||
switch mode {
|
||||
case .nag:
|
||||
self.backgroundColor = UIColor.ows_reminderYellow
|
||||
textColor = UIColor.ows_gray90
|
||||
iconColor = UIColor.ows_gray60
|
||||
case .explanation:
|
||||
// TODO: Theme, review with design.
|
||||
self.backgroundColor = Colors.unimportant
|
||||
textColor = Colors.text
|
||||
iconColor = Colors.separator
|
||||
}
|
||||
self.clipsToBounds = true
|
||||
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(gestureRecognizer:)))
|
||||
self.addGestureRecognizer(tapGesture)
|
||||
|
||||
let container = UIStackView()
|
||||
container.axis = .horizontal
|
||||
container.alignment = .center
|
||||
container.isLayoutMarginsRelativeArrangement = true
|
||||
|
||||
self.addSubview(container)
|
||||
container.layoutMargins = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
|
||||
container.ows_autoPinToSuperviewEdges()
|
||||
|
||||
// Label
|
||||
label.font = UIFont.ows_dynamicTypeSubheadline
|
||||
container.addArrangedSubview(label)
|
||||
label.textColor = textColor
|
||||
label.numberOfLines = 0
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
|
||||
// Show the disclosure indicator if this reminder has a tap action.
|
||||
if tapAction != nil {
|
||||
// Icon
|
||||
let iconName = (CurrentAppContext().isRTL ? "system_disclosure_indicator_rtl" : "system_disclosure_indicator")
|
||||
guard let iconImage = UIImage(named: iconName) else {
|
||||
owsFailDebug("missing icon.")
|
||||
return
|
||||
}
|
||||
let iconView = UIImageView(image: iconImage.withRenderingMode(.alwaysTemplate))
|
||||
iconView.contentMode = .scaleAspectFit
|
||||
iconView.tintColor = iconColor
|
||||
iconView.autoSetDimension(.width, toSize: 13)
|
||||
container.addArrangedSubview(iconView)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleTap(gestureRecognizer: UIGestureRecognizer) {
|
||||
guard gestureRecognizer.state == .recognized else {
|
||||
return
|
||||
}
|
||||
tapAction?()
|
||||
}
|
||||
}
|
|
@ -1,14 +1,18 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
final class ScanQRCodeWrapperVC : BaseVC {
|
||||
var delegate: (UIViewController & OWSQRScannerDelegate)? = nil
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
|
||||
final class ScanQRCodeWrapperVC: BaseVC {
|
||||
var delegate: (UIViewController & QRScannerDelegate)? = nil
|
||||
var isPresentedModally = false
|
||||
private let message: String
|
||||
private let scanQRCodeVC = OWSQRCodeScanningViewController()
|
||||
private let scanQRCodeVC = QRCodeScanningViewController()
|
||||
|
||||
// MARK: Settings
|
||||
override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait }
|
||||
|
||||
// MARK: Lifecycle
|
||||
// MARK: - Lifecycle
|
||||
|
||||
init(message: String) {
|
||||
self.message = message
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
@ -24,6 +28,9 @@ final class ScanQRCodeWrapperVC : BaseVC {
|
|||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
title = "Scan QR Code"
|
||||
|
||||
// Set up navigation bar if needed
|
||||
if isPresentedModally {
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(close))
|
||||
|
@ -37,6 +44,7 @@ final class ScanQRCodeWrapperVC : BaseVC {
|
|||
scanQRCodeVCView.pin(.trailing, to: .trailing, of: view)
|
||||
scanQRCodeVCView.autoPinEdge(.top, to: .top, of: view)
|
||||
scanQRCodeVCView.autoPinToSquareAspectRatio()
|
||||
|
||||
// Set up bottom view
|
||||
let bottomView = UIView()
|
||||
view.addSubview(bottomView)
|
||||
|
@ -44,37 +52,35 @@ final class ScanQRCodeWrapperVC : BaseVC {
|
|||
bottomView.pin(.leading, to: .leading, of: view)
|
||||
bottomView.pin(.trailing, to: .trailing, of: view)
|
||||
bottomView.pin(.bottom, to: .bottom, of: view)
|
||||
|
||||
// Set up explanation label
|
||||
let explanationLabel = UILabel()
|
||||
explanationLabel.text = message
|
||||
explanationLabel.textColor = Colors.text
|
||||
explanationLabel.themeTextColor = .textPrimary
|
||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
explanationLabel.numberOfLines = 0
|
||||
explanationLabel.lineBreakMode = .byWordWrapping
|
||||
explanationLabel.textAlignment = .center
|
||||
explanationLabel.lineBreakMode = .byWordWrapping
|
||||
explanationLabel.numberOfLines = 0
|
||||
bottomView.addSubview(explanationLabel)
|
||||
explanationLabel.autoPinWidthToSuperview(withMargin: 32)
|
||||
explanationLabel.autoPinHeightToSuperview(withMargin: 32)
|
||||
// Title
|
||||
title = "Scan QR Code"
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
UIDevice.current.ows_setOrientation(.portrait)
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.scanQRCodeVC.startCapture()
|
||||
}
|
||||
|
||||
self.scanQRCodeVC.startCapture()
|
||||
}
|
||||
|
||||
// MARK: Interaction
|
||||
// MARK: - Interaction
|
||||
|
||||
@objc private func close() {
|
||||
presentingViewController?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
public func startCapture() {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.scanQRCodeVC.startCapture()
|
||||
}
|
||||
self.scanQRCodeVC.startCapture()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ public class ConfirmationModal: Modal {
|
|||
attributedExplanation: NSAttributedString? = nil,
|
||||
stateToShow: State = .always,
|
||||
confirmTitle: String? = nil,
|
||||
confirmStyle: ThemeValue = .textPrimary,
|
||||
confirmStyle: ThemeValue = .alert_text,
|
||||
cancelTitle: String = "TXT_CANCEL_TITLE".localized(),
|
||||
cancelStyle: ThemeValue = .danger,
|
||||
dismissOnConfirm: Bool = true,
|
||||
|
@ -115,7 +115,7 @@ public class ConfirmationModal: Modal {
|
|||
private lazy var titleLabel: UILabel = {
|
||||
let result: UILabel = UILabel()
|
||||
result.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
||||
result.themeTextColor = .textPrimary
|
||||
result.themeTextColor = .alert_text
|
||||
result.textAlignment = .center
|
||||
result.lineBreakMode = .byWordWrapping
|
||||
result.numberOfLines = 0
|
||||
|
@ -126,7 +126,7 @@ public class ConfirmationModal: Modal {
|
|||
private lazy var explanationLabel: UILabel = {
|
||||
let result: UILabel = UILabel()
|
||||
result.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
result.themeTextColor = .textPrimary
|
||||
result.themeTextColor = .alert_text
|
||||
result.textAlignment = .center
|
||||
result.lineBreakMode = .byWordWrapping
|
||||
result.numberOfLines = 0
|
||||
|
|
|
@ -58,6 +58,13 @@ public class Modal: BaseVC, UIGestureRecognizerDelegate {
|
|||
self.afterClosed = afterClosed
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
// Ensure the modal doesn't crash on iPad when being presented
|
||||
if UIDevice.current.isIPad {
|
||||
self.popoverPresentationController?.permittedArrowDirections = []
|
||||
self.popoverPresentationController?.sourceView = self.view
|
||||
self.popoverPresentationController?.sourceRect = self.view.bounds
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
|
||||
class Sheet : BaseVC {
|
||||
private(set) var bottomConstraint: NSLayoutConstraint!
|
||||
|
||||
// MARK: Settings
|
||||
let overshoot: CGFloat = 40
|
||||
class var isDismissable: Bool { true }
|
||||
|
||||
// MARK: Components
|
||||
lazy var contentView: UIView = {
|
||||
let result = UIView()
|
||||
result.backgroundColor = Colors.modalBackground
|
||||
result.layer.cornerRadius = 24
|
||||
result.layer.masksToBounds = false
|
||||
result.layer.borderColor = isLightMode ? UIColor.white.cgColor : Colors.modalBorder.cgColor
|
||||
result.layer.borderWidth = 1
|
||||
result.layer.shadowColor = UIColor.black.cgColor
|
||||
result.layer.shadowRadius = isLightMode ? 2 : 8
|
||||
result.layer.shadowOpacity = isLightMode ? 0.1 : 0.64
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: Lifecycle
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
let alpha = isLightMode ? CGFloat(0.1) : Values.highOpacity
|
||||
view.backgroundColor = UIColor(hex: 0x000000).withAlphaComponent(alpha)
|
||||
if type(of: self).isDismissable {
|
||||
let swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(close))
|
||||
swipeGestureRecognizer.direction = .down
|
||||
view.addGestureRecognizer(swipeGestureRecognizer)
|
||||
}
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
private func setUpViewHierarchy() {
|
||||
view.addSubview(contentView)
|
||||
contentView.pin(.leading, to: .leading, of: view, withInset: -1)
|
||||
contentView.pin(.trailing, to: .trailing, of: view, withInset: 1)
|
||||
bottomConstraint = contentView.pin(.bottom, to: .bottom, of: view, withInset: overshoot)
|
||||
populateContentView()
|
||||
}
|
||||
|
||||
/// To be overridden by subclasses.
|
||||
func populateContentView() {
|
||||
preconditionFailure("populateContentView() is abstract and must be overridden.")
|
||||
}
|
||||
|
||||
// MARK: Interaction
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
let touch = touches.first!
|
||||
let location = touch.location(in: view)
|
||||
if contentView.frame.contains(location) {
|
||||
super.touchesBegan(touches, with: event)
|
||||
} else {
|
||||
if type(of: self).isDismissable {
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func close() {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
|
@ -7,8 +7,6 @@
|
|||
#import "Session-Swift.h"
|
||||
#import <MobileCoreServices/UTCoreTypes.h>
|
||||
|
||||
#import <SignalUtilitiesKit/UIUtil.h>
|
||||
|
||||
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
|
|
@ -21,7 +21,7 @@ import UIKit
|
|||
owsFailDebug("Missing root view controller.")
|
||||
return nil
|
||||
}
|
||||
return viewController.findFrontmostViewController(ignoringAlerts)
|
||||
return viewController.findFrontmostViewController(ignoringAlerts: ignoringAlerts)
|
||||
}
|
||||
|
||||
func openSystemSettings() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
|
||||
extension UINavigationBar {
|
||||
func generateSnapshot(in coordinateSpace: UICoordinateSpace) -> (UIView, CGRect)? {
|
||||
|
@ -31,7 +32,7 @@ extension UINavigationBar {
|
|||
height: frame.maxY
|
||||
)
|
||||
)
|
||||
snapshotView.backgroundColor = backgroundColor
|
||||
snapshotView.themeBackgroundColorForced = self.themeBackgroundColorForced
|
||||
|
||||
let imageView: UIImageView = UIImageView(image: image)
|
||||
imageView.frame = frame
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
|
||||
extension UIView {
|
||||
|
||||
struct CircularGlowConfiguration {
|
||||
let size: CGFloat
|
||||
let color: UIColor
|
||||
let isAnimated: Bool
|
||||
let animationDuration: TimeInterval
|
||||
let offset: CGSize
|
||||
let opacity: Float
|
||||
let radius: CGFloat
|
||||
|
||||
init(size: CGFloat, color: UIColor, isAnimated: Bool = false, animationDuration: TimeInterval = 0.25, offset: CGSize = CGSize(width: 0, height: 0.8), opacity: Float = isLightMode ? 0.4 : 1, radius: CGFloat) {
|
||||
self.size = size
|
||||
self.color = color
|
||||
self.isAnimated = isAnimated
|
||||
self.animationDuration = animationDuration
|
||||
self.offset = offset
|
||||
self.opacity = opacity
|
||||
self.radius = radius
|
||||
}
|
||||
}
|
||||
|
||||
func setCircularGlow(with configuration: CircularGlowConfiguration) {
|
||||
let newSize = configuration.size
|
||||
let newPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint.zero, size: CGSize(width: newSize, height: newSize))).cgPath
|
||||
if configuration.isAnimated {
|
||||
let pathAnimation = CABasicAnimation(keyPath: "shadowPath")
|
||||
pathAnimation.fromValue = layer.shadowPath
|
||||
pathAnimation.toValue = newPath
|
||||
pathAnimation.duration = configuration.animationDuration
|
||||
layer.add(pathAnimation, forKey: pathAnimation.keyPath)
|
||||
}
|
||||
layer.shadowPath = newPath
|
||||
let newColor = configuration.color.cgColor
|
||||
if configuration.isAnimated {
|
||||
let colorAnimation = CABasicAnimation(keyPath: "shadowColor")
|
||||
colorAnimation.fromValue = layer.shadowColor
|
||||
colorAnimation.toValue = newColor
|
||||
colorAnimation.duration = configuration.animationDuration
|
||||
layer.add(colorAnimation, forKey: colorAnimation.keyPath)
|
||||
}
|
||||
layer.shadowColor = newColor
|
||||
layer.shadowOffset = configuration.offset
|
||||
layer.shadowOpacity = configuration.opacity
|
||||
layer.shadowRadius = configuration.radius
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@
|
|||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <Photos/Photos.h>
|
||||
#import <SignalCoreKit/Threading.h>
|
||||
#import <SignalUtilitiesKit/UIUtil.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ public struct RecipientState: Codable, Equatable, FetchableRecord, PersistableRe
|
|||
}
|
||||
}
|
||||
|
||||
public func statusIconInfo(variant: Interaction.Variant, hasAtLeastOneReadReceipt: Bool) -> (image: UIImage?, tintColor: ThemeValue) {
|
||||
public func statusIconInfo(variant: Interaction.Variant, hasAtLeastOneReadReceipt: Bool) -> (image: UIImage?, themeTintColor: ThemeValue) {
|
||||
guard variant == .standardOutgoing else { return (nil, .textPrimary) }
|
||||
|
||||
switch (self, hasAtLeastOneReadReceipt) {
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString *const OWSWindowManagerCallDidChangeNotification;
|
||||
|
||||
extern NSString *const IsScreenBlockActiveDidChangeNotification;
|
||||
|
||||
// This VC can become first responder
|
||||
|
@ -32,27 +30,10 @@ extern const UIWindowLevel UIWindowLevel_Background;
|
|||
- (void)setupWithRootWindow:(UIWindow *)rootWindow screenBlockingWindow:(UIWindow *)screenBlockingWindow;
|
||||
|
||||
@property (nonatomic, readonly) UIWindow *rootWindow;
|
||||
@property (nonatomic, readonly) UIWindow *menuActionsWindow;
|
||||
@property (nonatomic) BOOL isScreenBlockActive;
|
||||
|
||||
- (BOOL)isAppWindow:(UIWindow *)window;
|
||||
|
||||
#pragma mark - Message Actions
|
||||
|
||||
@property (nonatomic, readonly) BOOL isPresentingMenuActions;
|
||||
|
||||
- (void)showMenuActionsWindow:(UIViewController *)menuActionsViewController;
|
||||
- (void)hideMenuActionsWindow;
|
||||
|
||||
#pragma mark - Calls
|
||||
|
||||
@property (nonatomic, readonly) BOOL shouldShowCallView;
|
||||
|
||||
- (void)startCall:(UIViewController *)callViewController;
|
||||
- (void)endCall:(UIViewController *)callViewController;
|
||||
- (void)leaveCallView;
|
||||
- (BOOL)hasCall;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -8,31 +8,11 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const OWSWindowManagerCallDidChangeNotification = @"OWSWindowManagerCallDidChangeNotification";
|
||||
|
||||
NSString *const IsScreenBlockActiveDidChangeNotification = @"IsScreenBlockActiveDidChangeNotification";
|
||||
|
||||
const CGFloat OWSWindowManagerCallBannerHeight(void)
|
||||
{
|
||||
return CurrentAppContext().statusBarHeight + 20;
|
||||
}
|
||||
|
||||
// Behind everything, especially the root window.
|
||||
const UIWindowLevel UIWindowLevel_Background = -1.f;
|
||||
|
||||
const UIWindowLevel UIWindowLevel_ReturnToCall(void);
|
||||
const UIWindowLevel UIWindowLevel_ReturnToCall(void)
|
||||
{
|
||||
return UIWindowLevelStatusBar - 1;
|
||||
}
|
||||
|
||||
// In front of the root window, behind the screen blocking window.
|
||||
const UIWindowLevel UIWindowLevel_CallView(void);
|
||||
const UIWindowLevel UIWindowLevel_CallView(void)
|
||||
{
|
||||
return UIWindowLevelNormal + 1.f;
|
||||
}
|
||||
|
||||
// In front of the status bar and CallView
|
||||
const UIWindowLevel UIWindowLevel_ScreenBlocking(void);
|
||||
const UIWindowLevel UIWindowLevel_ScreenBlocking(void)
|
||||
|
@ -40,36 +20,6 @@ const UIWindowLevel UIWindowLevel_ScreenBlocking(void)
|
|||
return UIWindowLevelStatusBar + 2.f;
|
||||
}
|
||||
|
||||
// In front of everything
|
||||
const UIWindowLevel UIWindowLevel_MessageActions(void);
|
||||
const UIWindowLevel UIWindowLevel_MessageActions(void)
|
||||
{
|
||||
// Note: To cover the keyboard, this is higher than the ScreenBlocking level,
|
||||
// but this window is hidden when screen protection is shown.
|
||||
return CGFLOAT_MAX - 100;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface MessageActionsWindow : UIWindow
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation MessageActionsWindow
|
||||
|
||||
- (UIWindowLevel)windowLevel
|
||||
{
|
||||
// As of iOS11, setWindowLevel clamps the value below
|
||||
// the height of the keyboard window.
|
||||
// Because we want to display above the keyboard, we hardcode
|
||||
// the `windowLevel` getter.
|
||||
return UIWindowLevel_MessageActions();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation OWSWindowRootViewController
|
||||
|
@ -114,22 +64,10 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
// UIWindowLevelNormal
|
||||
@property (nonatomic) UIWindow *rootWindow;
|
||||
|
||||
// UIWindowLevel_CallView
|
||||
@property (nonatomic) UIWindow *callViewWindow;
|
||||
@property (nonatomic) UINavigationController *callNavigationController;
|
||||
|
||||
// UIWindowLevel_MessageActions
|
||||
@property (nonatomic) UIWindow *menuActionsWindow;
|
||||
@property (nonatomic, nullable) UIViewController *menuActionsViewController;
|
||||
|
||||
// UIWindowLevel_Background if inactive,
|
||||
// UIWindowLevel_ScreenBlocking() if active.
|
||||
@property (nonatomic) UIWindow *screenBlockingWindow;
|
||||
|
||||
@property (nonatomic) BOOL shouldShowCallView;
|
||||
|
||||
@property (nonatomic, nullable) UIViewController *callViewController;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
@ -156,10 +94,7 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
{
|
||||
self.rootWindow = rootWindow;
|
||||
self.screenBlockingWindow = screenBlockingWindow;
|
||||
|
||||
self.callViewWindow = [self createCallViewWindow:rootWindow];
|
||||
self.menuActionsWindow = [self createMenuActionsWindowWithRoowWindow:rootWindow];
|
||||
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(didChangeStatusBarFrame:)
|
||||
name:UIApplicationDidChangeStatusBarFrameNotification
|
||||
|
@ -175,47 +110,10 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
|
||||
- (void)didChangeStatusBarFrame:(NSNotification *)notification
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification
|
||||
{
|
||||
[self hideMenuActionsWindow];
|
||||
}
|
||||
|
||||
- (UIWindow *)createMenuActionsWindowWithRoowWindow:(UIWindow *)rootWindow
|
||||
{
|
||||
UIWindow *window = [[MessageActionsWindow alloc] initWithFrame:rootWindow.bounds];
|
||||
window.hidden = YES;
|
||||
window.backgroundColor = UIColor.clearColor;
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
- (UIWindow *)createCallViewWindow:(UIWindow *)rootWindow
|
||||
{
|
||||
UIWindow *window = [[UIWindow alloc] initWithFrame:rootWindow.bounds];
|
||||
window.hidden = YES;
|
||||
window.windowLevel = UIWindowLevel_CallView();
|
||||
window.opaque = YES;
|
||||
// TODO: What's the right color to use here?
|
||||
window.backgroundColor = [UIColor blackColor];
|
||||
|
||||
UIViewController *viewController = [OWSWindowRootViewController new];
|
||||
viewController.view.backgroundColor = [UIColor blackColor];
|
||||
|
||||
// NOTE: Do not use OWSNavigationController for call window.
|
||||
// It adjusts the size of the navigation bar to reflect the
|
||||
// call window. We don't want those adjustments made within
|
||||
// the call window itself.
|
||||
OWSWindowRootNavigationViewController *navigationController =
|
||||
[[OWSWindowRootNavigationViewController alloc] initWithRootViewController:viewController];
|
||||
navigationController.navigationBarHidden = YES;
|
||||
self.callNavigationController = navigationController;
|
||||
|
||||
window.rootViewController = navigationController;
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
- (void)setIsScreenBlockActive:(BOOL)isScreenBlockActive
|
||||
|
@ -231,92 +129,7 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
|
||||
- (BOOL)isAppWindow:(UIWindow *)window
|
||||
{
|
||||
return (window == self.rootWindow || window == self.callViewWindow
|
||||
|| window == self.menuActionsWindow || window == self.screenBlockingWindow);
|
||||
}
|
||||
|
||||
#pragma mark - Message Actions
|
||||
|
||||
- (BOOL)isPresentingMenuActions
|
||||
{
|
||||
return self.menuActionsViewController != nil;
|
||||
}
|
||||
|
||||
- (void)showMenuActionsWindow:(UIViewController *)menuActionsViewController
|
||||
{
|
||||
self.menuActionsViewController = menuActionsViewController;
|
||||
self.menuActionsWindow.rootViewController = menuActionsViewController;
|
||||
|
||||
[self ensureWindowState];
|
||||
}
|
||||
|
||||
- (void)hideMenuActionsWindow
|
||||
{
|
||||
self.menuActionsWindow.rootViewController = nil;
|
||||
self.menuActionsViewController = nil;
|
||||
|
||||
[self ensureWindowState];
|
||||
}
|
||||
|
||||
#pragma mark - Calls
|
||||
|
||||
- (void)setCallViewController:(nullable UIViewController *)callViewController
|
||||
{
|
||||
if (callViewController == _callViewController) {
|
||||
return;
|
||||
}
|
||||
|
||||
_callViewController = callViewController;
|
||||
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:OWSWindowManagerCallDidChangeNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)startCall:(UIViewController *)callViewController
|
||||
{
|
||||
self.callViewController = callViewController;
|
||||
|
||||
// Attach callViewController to window.
|
||||
[self.callNavigationController popToRootViewControllerAnimated:NO];
|
||||
[self.callNavigationController pushViewController:callViewController animated:NO];
|
||||
self.shouldShowCallView = YES;
|
||||
// CallViewController only supports portrait, but if we're _already_ landscape it won't
|
||||
// automatically switch.
|
||||
[UIDevice.currentDevice ows_setOrientation:UIInterfaceOrientationPortrait];
|
||||
[self ensureWindowState];
|
||||
}
|
||||
|
||||
- (void)endCall:(UIViewController *)callViewController
|
||||
{
|
||||
if (self.callViewController != callViewController) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dettach callViewController from window.
|
||||
[self.callNavigationController popToRootViewControllerAnimated:NO];
|
||||
self.callViewController = nil;
|
||||
|
||||
self.shouldShowCallView = NO;
|
||||
|
||||
[self ensureWindowState];
|
||||
}
|
||||
|
||||
- (void)leaveCallView
|
||||
{
|
||||
self.shouldShowCallView = NO;
|
||||
|
||||
[self ensureWindowState];
|
||||
}
|
||||
|
||||
- (void)showCallView
|
||||
{
|
||||
self.shouldShowCallView = YES;
|
||||
|
||||
[self ensureWindowState];
|
||||
}
|
||||
|
||||
- (BOOL)hasCall
|
||||
{
|
||||
return self.callViewController != nil;
|
||||
return (window == self.rootWindow || window == self.screenBlockingWindow);
|
||||
}
|
||||
|
||||
#pragma mark - Window State
|
||||
|
@ -332,30 +145,13 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
// Show Screen Block.
|
||||
|
||||
[self ensureRootWindowHidden];
|
||||
[self ensureCallViewWindowHidden];
|
||||
[self ensureMessageActionsWindowHidden];
|
||||
[self ensureScreenBlockWindowShown];
|
||||
} else if (self.callViewController && self.shouldShowCallView) {
|
||||
// Show Call View.
|
||||
|
||||
[self ensureRootWindowHidden];
|
||||
[self ensureCallViewWindowShown];
|
||||
[self ensureMessageActionsWindowHidden];
|
||||
[self ensureScreenBlockWindowHidden];
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Show Root Window
|
||||
|
||||
[self ensureRootWindowShown];
|
||||
[self ensureCallViewWindowHidden];
|
||||
[self ensureScreenBlockWindowHidden];
|
||||
|
||||
if (self.menuActionsViewController) {
|
||||
// Add "Message Actions" action sheet
|
||||
|
||||
[self ensureMessageActionsWindowShown];
|
||||
} else {
|
||||
[self ensureMessageActionsWindowHidden];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,27 +170,6 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
self.rootWindow.hidden = YES;
|
||||
}
|
||||
|
||||
- (void)ensureCallViewWindowShown
|
||||
{
|
||||
[self.callViewWindow makeKeyAndVisible];
|
||||
}
|
||||
|
||||
- (void)ensureCallViewWindowHidden
|
||||
{
|
||||
self.callViewWindow.hidden = YES;
|
||||
}
|
||||
|
||||
- (void)ensureMessageActionsWindowShown
|
||||
{
|
||||
// Do not make key, we want the keyboard to stay popped.
|
||||
self.menuActionsWindow.hidden = NO;
|
||||
}
|
||||
|
||||
- (void)ensureMessageActionsWindowHidden
|
||||
{
|
||||
self.menuActionsWindow.hidden = YES;
|
||||
}
|
||||
|
||||
- (void)ensureScreenBlockWindowShown
|
||||
{
|
||||
self.screenBlockingWindow.windowLevel = UIWindowLevel_ScreenBlocking();
|
||||
|
|
|
@ -62,20 +62,20 @@ final class SAEScreenLockViewController: ScreenLockViewController {
|
|||
ThemeManager.onThemeChange(observer: self.unlockButton) { [weak self] theme, _ in
|
||||
switch theme.interfaceStyle {
|
||||
case .light:
|
||||
self?.unlockButton.setTitleColor(theme.colors[.textPrimary], for: .normal)
|
||||
self?.unlockButton.setBackgroundImage(
|
||||
theme.colors[.textPrimary]?.withAlphaComponent(0.3).toImage(),
|
||||
self?.unlockButton.setThemeTitleColorForced(.theme(theme, color: .textPrimary), for: .normal)
|
||||
self?.unlockButton.setThemeBackgroundColorForced(
|
||||
.theme(theme, color: .textPrimary),
|
||||
for: .highlighted
|
||||
)
|
||||
self?.unlockButton.layer.borderColor = theme.colors[.textPrimary]?.cgColor
|
||||
self?.unlockButton.themeBorderColorForced = .theme(theme, color: .textPrimary)
|
||||
|
||||
default:
|
||||
self?.unlockButton.setTitleColor(Theme.PrimaryColor.green.color, for: .normal)
|
||||
self?.unlockButton.setBackgroundImage(
|
||||
Theme.PrimaryColor.green.color.withAlphaComponent(0.3).toImage(),
|
||||
self?.unlockButton.setThemeTitleColorForced(.primary(.green), for: .normal)
|
||||
self?.unlockButton.setThemeBackgroundColorForced(
|
||||
.primary(.green, alpha: 0.3),
|
||||
for: .highlighted
|
||||
)
|
||||
self?.unlockButton.layer.borderColor = Theme.PrimaryColor.green.color.cgColor
|
||||
self?.unlockButton.themeBorderColorForced = .primary(.green)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ final class ShareAppExtensionContext: NSObject, AppContext {
|
|||
}
|
||||
|
||||
func frontmostViewController() -> UIViewController? {
|
||||
return rootViewController.findFrontmostViewController(true)
|
||||
return rootViewController.findFrontmostViewController(ignoringAlerts: true)
|
||||
}
|
||||
|
||||
func appDocumentDirectoryPath() -> String {
|
||||
|
|
|
@ -25,7 +25,7 @@ class NotificationContentViewModelSpec: QuickSpec {
|
|||
SNUtilitiesKit.migrations(),
|
||||
SNSnodeKit.migrations(),
|
||||
SNMessagingKit.migrations(),
|
||||
SUIKit.migrations()
|
||||
SNUIKit.migrations()
|
||||
]
|
||||
)
|
||||
viewModel = NotificationContentViewModel(storage: mockStorage)
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
import Foundation
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public enum SUIKit {
|
||||
public enum SNUIKit {
|
||||
public static func migrations() -> TargetMigrations {
|
||||
return TargetMigrations(
|
||||
identifier: .uiKit,
|
||||
migrations: [
|
||||
// Want to ensure the initial DB stuff has been completed before doing any
|
||||
// SUIKit migrations
|
||||
// SNUIKit migrations
|
||||
[], // Initial DB Creation
|
||||
[], // YDB to GRDB Migration
|
||||
[], // YDB Removal
|
||||
|
|
|
@ -82,7 +82,10 @@ public enum ThemeManager {
|
|||
|
||||
// Note: We have to trigger this directly or the 'TraitObservingWindow' won't actually
|
||||
// trigger the trait change if the app launched with this setting switched off
|
||||
applyWindowStyling()
|
||||
|
||||
// Note: We need to set this to 'unspecified' to force the UI to properly update as the
|
||||
// 'TraitObservingWindow' won't actually trigger the trait change otherwise
|
||||
mainWindow?.overrideUserInterfaceStyle = .unspecified
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,8 +206,6 @@ public enum ThemeManager {
|
|||
|
||||
public static func applyWindowStyling() {
|
||||
mainWindow?.overrideUserInterfaceStyle = {
|
||||
guard !ThemeManager.matchSystemNightModeSetting else { return .unspecified }
|
||||
|
||||
switch ThemeManager.currentTheme.interfaceStyle {
|
||||
case .light: return .light
|
||||
case .dark, .unspecified: return .dark
|
||||
|
|
|
@ -74,6 +74,7 @@ internal enum Theme_ClassicDark: ThemeColors {
|
|||
.appearance_buttonHighlight: .classicDark3,
|
||||
|
||||
// Alert
|
||||
.alert_text: .classicDark6,
|
||||
.alert_background: .classicDark1,
|
||||
.alert_buttonBackground: .classicDark1,
|
||||
.alert_buttonHighlight: .classicDark3,
|
||||
|
@ -96,6 +97,7 @@ internal enum Theme_ClassicDark: ThemeColors {
|
|||
// ContextMenu
|
||||
.contextMenu_background: .classicDark1,
|
||||
.contextMenu_highlight: .primary,
|
||||
.contextMenu_text: .classicDark6,
|
||||
.contextMenu_textHighlight: .classicDark0,
|
||||
|
||||
// Call
|
||||
|
@ -104,6 +106,6 @@ internal enum Theme_ClassicDark: ThemeColors {
|
|||
|
||||
// Reactions
|
||||
.reactions_contextBackground: .classicDark2,
|
||||
.reactions_contextMoreBackground: .classicDark3
|
||||
.reactions_contextMoreBackground: .classicDark1
|
||||
]
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ internal enum Theme_ClassicLight: ThemeColors {
|
|||
.appearance_buttonHighlight: .classicLight4,
|
||||
|
||||
// Alert
|
||||
.alert_text: .classicLight0,
|
||||
.alert_background: .classicLight6,
|
||||
.alert_buttonBackground: .classicLight6,
|
||||
.alert_buttonHighlight: .classicLight4,
|
||||
|
@ -96,6 +97,7 @@ internal enum Theme_ClassicLight: ThemeColors {
|
|||
// ContextMenu
|
||||
.contextMenu_background: .classicLight6,
|
||||
.contextMenu_highlight: .primary,
|
||||
.contextMenu_text: .classicLight0,
|
||||
.contextMenu_textHighlight: .classicLight0,
|
||||
|
||||
// Call
|
||||
|
|
|
@ -75,13 +75,14 @@ internal extension UIColor {
|
|||
static let oceanDark5: UIColor = #colorLiteral(red: 0.6509803922, green: 0.662745098, blue: 0.8078431373, alpha: 1) // #A6A9CE
|
||||
static let oceanDark6: UIColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) // #FFFFFF
|
||||
|
||||
static let oceanLight0: UIColor = #colorLiteral(red: 0.09803921569, green: 0.2039215686, blue: 0.3647058824, alpha: 1) // #19345D
|
||||
static let oceanLight1: UIColor = #colorLiteral(red: 0.4156862745, green: 0.431372549, blue: 0.5647058824, alpha: 1) // #6A6E90
|
||||
static let oceanLight2: UIColor = #colorLiteral(red: 0.3607843137, green: 0.6666666667, blue: 0.8, alpha: 1) // #5CAACC
|
||||
static let oceanLight3: UIColor = #colorLiteral(red: 0.7019607843, green: 0.9294117647, blue: 0.9490196078, alpha: 1) // #B3EDF2
|
||||
static let oceanLight4: UIColor = #colorLiteral(red: 0.9058823529, green: 0.9529411765, blue: 0.9568627451, alpha: 1) // #E7F3F4
|
||||
static let oceanLight5: UIColor = #colorLiteral(red: 0.9254901961, green: 0.9803921569, blue: 0.9843137255, alpha: 1) // #ECFAFB
|
||||
static let oceanLight6: UIColor = #colorLiteral(red: 0.9882352941, green: 1, blue: 1, alpha: 1) // #FCFFFF
|
||||
static let oceanLight0: UIColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) // #000000
|
||||
static let oceanLight1: UIColor = #colorLiteral(red: 0.09803921569, green: 0.2039215686, blue: 0.3647058824, alpha: 1) // #19345D
|
||||
static let oceanLight2: UIColor = #colorLiteral(red: 0.4156862745, green: 0.431372549, blue: 0.5647058824, alpha: 1) // #6A6E90
|
||||
static let oceanLight3: UIColor = #colorLiteral(red: 0.3607843137, green: 0.6666666667, blue: 0.8, alpha: 1) // #5CAACC
|
||||
static let oceanLight4: UIColor = #colorLiteral(red: 0.7019607843, green: 0.9294117647, blue: 0.9490196078, alpha: 1) // #B3EDF2
|
||||
static let oceanLight5: UIColor = #colorLiteral(red: 0.9058823529, green: 0.9529411765, blue: 0.9568627451, alpha: 1) // #E7F3F4
|
||||
static let oceanLight6: UIColor = #colorLiteral(red: 0.9254901961, green: 0.9803921569, blue: 0.9843137255, alpha: 1) // #ECFAFB
|
||||
static let oceanLight7: UIColor = #colorLiteral(red: 0.9882352941, green: 1, blue: 1, alpha: 1) // #FCFFFF
|
||||
}
|
||||
|
||||
public extension UIColor {
|
||||
|
|
|
@ -74,6 +74,7 @@ internal enum Theme_OceanDark: ThemeColors {
|
|||
.appearance_buttonHighlight: .oceanDark4,
|
||||
|
||||
// Alert
|
||||
.alert_text: .oceanDark6,
|
||||
.alert_background: .oceanDark3,
|
||||
.alert_buttonBackground: .oceanDark3,
|
||||
.alert_buttonHighlight: .oceanDark4,
|
||||
|
@ -96,6 +97,7 @@ internal enum Theme_OceanDark: ThemeColors {
|
|||
// ContextMenu
|
||||
.contextMenu_background: .oceanDark2,
|
||||
.contextMenu_highlight: .primary,
|
||||
.contextMenu_text: .oceanDark6,
|
||||
.contextMenu_textHighlight: .oceanDark0,
|
||||
|
||||
// Call
|
||||
|
|
|
@ -12,27 +12,27 @@ internal enum Theme_OceanLight: ThemeColors {
|
|||
.defaultPrimary: Theme.PrimaryColor.blue.color,
|
||||
.danger: .dangerLight,
|
||||
.disabled: .disabledLight,
|
||||
.backgroundPrimary: .oceanLight6,
|
||||
.backgroundSecondary: .oceanLight5,
|
||||
.textPrimary: .oceanLight0,
|
||||
.textSecondary: .oceanLight1,
|
||||
.borderSeparator: .oceanLight2,
|
||||
.backgroundPrimary: .oceanLight7,
|
||||
.backgroundSecondary: .oceanLight6,
|
||||
.textPrimary: .oceanLight1,
|
||||
.textSecondary: .oceanLight2,
|
||||
.borderSeparator: .oceanLight3,
|
||||
|
||||
// Path
|
||||
.path_connected: .pathConnected,
|
||||
.path_connecting: .pathConnecting,
|
||||
.path_error: .pathError,
|
||||
.path_unknown: .oceanLight4,
|
||||
.path_unknown: .oceanLight5,
|
||||
|
||||
// TextBox
|
||||
.textBox_background: .oceanLight6,
|
||||
.textBox_border: .oceanLight2,
|
||||
.textBox_background: .oceanLight7,
|
||||
.textBox_border: .oceanLight3,
|
||||
|
||||
// MessageBubble
|
||||
.messageBubble_outgoingBackground: .primary,
|
||||
.messageBubble_incomingBackground: .oceanLight3,
|
||||
.messageBubble_outgoingText: .oceanLight0,
|
||||
.messageBubble_incomingText: .oceanLight0,
|
||||
.messageBubble_incomingBackground: .oceanLight4,
|
||||
.messageBubble_outgoingText: .oceanLight1,
|
||||
.messageBubble_incomingText: .oceanLight1,
|
||||
.messageBubble_overlay: .black_06,
|
||||
|
||||
// MenuButton
|
||||
|
@ -44,58 +44,60 @@ internal enum Theme_OceanLight: ThemeColors {
|
|||
// RadioButton
|
||||
.radioButton_selectedBackground: .primary,
|
||||
.radioButton_unselectedBackground: .clear,
|
||||
.radioButton_selectedBorder: .oceanLight0,
|
||||
.radioButton_unselectedBorder: .oceanLight2,
|
||||
.radioButton_selectedBorder: .oceanLight1,
|
||||
.radioButton_unselectedBorder: .oceanLight3,
|
||||
|
||||
// OutlineButton
|
||||
.outlineButton_text: .oceanLight0,
|
||||
.outlineButton_text: .oceanLight1,
|
||||
.outlineButton_background: .clear,
|
||||
.outlineButton_highlight: .oceanLight0.withAlphaComponent(0.1),
|
||||
.outlineButton_border: .oceanLight0,
|
||||
.outlineButton_filledText: .oceanLight6,
|
||||
.outlineButton_filledBackground: .oceanLight0,
|
||||
.outlineButton_filledHighlight: .oceanLight1,
|
||||
.outlineButton_highlight: .oceanLight1.withAlphaComponent(0.1),
|
||||
.outlineButton_border: .oceanLight1,
|
||||
.outlineButton_filledText: .oceanLight7,
|
||||
.outlineButton_filledBackground: .oceanLight1,
|
||||
.outlineButton_filledHighlight: .oceanLight2,
|
||||
.outlineButton_destructiveText: .dangerLight,
|
||||
.outlineButton_destructiveBackground: .clear,
|
||||
.outlineButton_destructiveHighlight: .dangerLight.withAlphaComponent(0.3),
|
||||
.outlineButton_destructiveBorder: .dangerLight,
|
||||
|
||||
// SolidButton
|
||||
.solidButton_background: .oceanLight4,
|
||||
.solidButton_highlight: .oceanLight5,
|
||||
.solidButton_background: .oceanLight5,
|
||||
.solidButton_highlight: .oceanLight6,
|
||||
|
||||
// Settings
|
||||
.settings_tabBackground: .oceanLight6,
|
||||
.settings_tabHighlight: .oceanLight4,
|
||||
.settings_tabBackground: .oceanLight7,
|
||||
.settings_tabHighlight: .oceanLight5,
|
||||
|
||||
// Appearance
|
||||
.appearance_sectionBackground: .oceanLight6,
|
||||
.appearance_buttonBackground: .oceanLight6,
|
||||
.appearance_buttonHighlight: .oceanLight4,
|
||||
.appearance_sectionBackground: .oceanLight7,
|
||||
.appearance_buttonBackground: .oceanLight7,
|
||||
.appearance_buttonHighlight: .oceanLight5,
|
||||
|
||||
// Alert
|
||||
.alert_background: .oceanLight6,
|
||||
.alert_buttonBackground: .oceanLight6,
|
||||
.alert_buttonHighlight: .oceanLight4,
|
||||
.alert_text: .oceanLight0,
|
||||
.alert_background: .oceanLight7,
|
||||
.alert_buttonBackground: .oceanLight7,
|
||||
.alert_buttonHighlight: .oceanLight5,
|
||||
|
||||
// ConversationButton
|
||||
.conversationButton_background: .oceanLight6,
|
||||
.conversationButton_highlight: .oceanLight4,
|
||||
.conversationButton_unreadBackground: .oceanLight5,
|
||||
.conversationButton_unreadHighlight: .oceanLight4,
|
||||
.conversationButton_background: .oceanLight7,
|
||||
.conversationButton_highlight: .oceanLight5,
|
||||
.conversationButton_unreadBackground: .oceanLight6,
|
||||
.conversationButton_unreadHighlight: .oceanLight5,
|
||||
.conversationButton_unreadStripBackground: .primary,
|
||||
.conversationButton_unreadBubbleBackground: .primary,
|
||||
.conversationButton_unreadBubbleText: .oceanLight0,
|
||||
.conversationButton_unreadBubbleText: .oceanLight1,
|
||||
.conversationButton_swipeDestructive: .dangerLight,
|
||||
.conversationButton_swipeSecondary: .oceanLight1,
|
||||
.conversationButton_swipeSecondary: .oceanLight2,
|
||||
.conversationButton_swipeTertiary: Theme.PrimaryColor.orange.color,
|
||||
|
||||
// InputButton
|
||||
.inputButton_background: .oceanLight6,
|
||||
.inputButton_background: .oceanLight7,
|
||||
|
||||
// ContextMenu
|
||||
.contextMenu_background: .oceanLight6,
|
||||
.contextMenu_background: .oceanLight7,
|
||||
.contextMenu_highlight: .primary,
|
||||
.contextMenu_text: .oceanLight0,
|
||||
.contextMenu_textHighlight: .oceanLight0,
|
||||
|
||||
// Call
|
||||
|
@ -103,7 +105,7 @@ internal enum Theme_OceanLight: ThemeColors {
|
|||
.callDecline_background: .dangerLight,
|
||||
|
||||
// Reactions
|
||||
.reactions_contextBackground: .oceanLight6,
|
||||
.reactions_contextMoreBackground: .oceanLight5
|
||||
.reactions_contextBackground: .oceanLight7,
|
||||
.reactions_contextMoreBackground: .oceanLight6
|
||||
]
|
||||
}
|
||||
|
|
|
@ -125,6 +125,7 @@ public enum ThemeValue {
|
|||
case appearance_buttonHighlight
|
||||
|
||||
// Alert
|
||||
case alert_text
|
||||
case alert_background
|
||||
case alert_buttonBackground
|
||||
case alert_buttonHighlight
|
||||
|
@ -147,6 +148,7 @@ public enum ThemeValue {
|
|||
// ContextMenu
|
||||
case contextMenu_background
|
||||
case contextMenu_highlight
|
||||
case contextMenu_text
|
||||
case contextMenu_textHighlight
|
||||
|
||||
// Call
|
||||
|
@ -157,3 +159,40 @@ public enum ThemeValue {
|
|||
case reactions_contextBackground
|
||||
case reactions_contextMoreBackground
|
||||
}
|
||||
|
||||
// MARK: - ForcedThemeValue
|
||||
|
||||
public enum ForcedThemeValue {
|
||||
case color(UIColor)
|
||||
case primary(Theme.PrimaryColor, alpha: CGFloat?)
|
||||
case theme(Theme, color: ThemeValue, alpha: CGFloat?)
|
||||
|
||||
public static func primary(_ primary: Theme.PrimaryColor) -> ForcedThemeValue {
|
||||
return .primary(primary, alpha: nil)
|
||||
}
|
||||
|
||||
public static func theme(_ theme: Theme, color: ThemeValue) -> ForcedThemeValue {
|
||||
return .theme(theme, color: color, alpha: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ForcedThemeAttribute
|
||||
|
||||
public enum ForcedThemeAttribute {
|
||||
case background(UIColor)
|
||||
case foreground(UIColor)
|
||||
|
||||
public var key: NSAttributedString.Key {
|
||||
switch self {
|
||||
case .background: return NSAttributedString.Key.backgroundColor
|
||||
case .foreground: return NSAttributedString.Key.foregroundColor
|
||||
}
|
||||
}
|
||||
|
||||
public var value: Any {
|
||||
switch self {
|
||||
case .background(let value): return value
|
||||
case .foreground(let value): return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,20 +8,124 @@ public extension UIView {
|
|||
get { return nil }
|
||||
}
|
||||
|
||||
var themeBackgroundColorForced: ForcedThemeValue? {
|
||||
set {
|
||||
switch newValue {
|
||||
case .color(let color): backgroundColor = color
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
backgroundColor = value.color
|
||||
return
|
||||
}
|
||||
|
||||
backgroundColor = value.color.withAlphaComponent(alpha)
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
backgroundColor = theme.colors[value]
|
||||
return
|
||||
}
|
||||
|
||||
backgroundColor = theme.colors[value]?.withAlphaComponent(alpha)
|
||||
|
||||
case .none: backgroundColor = nil
|
||||
}
|
||||
}
|
||||
get { return self.backgroundColor.map { .color($0) } }
|
||||
}
|
||||
|
||||
var themeTintColor: ThemeValue? {
|
||||
set { ThemeManager.set(self, keyPath: \.tintColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
|
||||
var themeTintColorForced: ForcedThemeValue? {
|
||||
set {
|
||||
switch newValue {
|
||||
case .color(let color): tintColor = color
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
tintColor = value.color
|
||||
return
|
||||
}
|
||||
|
||||
tintColor = value.color.withAlphaComponent(alpha)
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
tintColor = theme.colors[value]
|
||||
return
|
||||
}
|
||||
|
||||
tintColor = theme.colors[value]?.withAlphaComponent(alpha)
|
||||
|
||||
case .none: tintColor = nil
|
||||
}
|
||||
}
|
||||
get { return self.tintColor.map { .color($0) } }
|
||||
}
|
||||
|
||||
var themeBorderColor: ThemeValue? {
|
||||
set { ThemeManager.set(self, keyPath: \.layer.borderColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
|
||||
var themeBorderColorForced: ForcedThemeValue? {
|
||||
set {
|
||||
switch newValue {
|
||||
case .color(let color): layer.borderColor = color.cgColor
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
layer.borderColor = value.color.cgColor
|
||||
return
|
||||
}
|
||||
|
||||
layer.borderColor = value.color.withAlphaComponent(alpha).cgColor
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
layer.borderColor = theme.colors[value]?.cgColor
|
||||
return
|
||||
}
|
||||
|
||||
layer.borderColor = theme.colors[value]?.withAlphaComponent(alpha).cgColor
|
||||
|
||||
case .none: layer.borderColor = nil
|
||||
}
|
||||
}
|
||||
get { return nil }
|
||||
}
|
||||
|
||||
var themeShadowColor: ThemeValue? {
|
||||
set { ThemeManager.set(self, keyPath: \.layer.shadowColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
|
||||
var themeShadowColorForced: ForcedThemeValue? {
|
||||
set {
|
||||
switch newValue {
|
||||
case .color(let color): layer.shadowColor = color.cgColor
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
layer.shadowColor = value.color.cgColor
|
||||
return
|
||||
}
|
||||
|
||||
layer.shadowColor = value.color.withAlphaComponent(alpha).cgColor
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
layer.shadowColor = theme.colors[value]?.cgColor
|
||||
return
|
||||
}
|
||||
|
||||
layer.shadowColor = theme.colors[value]?.withAlphaComponent(alpha).cgColor
|
||||
|
||||
case .none: layer.shadowColor = nil
|
||||
}
|
||||
}
|
||||
get { return self.layer.shadowColor.map { .color(UIColor(cgColor: $0)) } }
|
||||
}
|
||||
}
|
||||
|
||||
public extension UILabel {
|
||||
|
@ -29,6 +133,32 @@ public extension UILabel {
|
|||
set { ThemeManager.set(self, keyPath: \.textColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
|
||||
var themeTextColorForced: ForcedThemeValue? {
|
||||
set {
|
||||
switch newValue {
|
||||
case .color(let color): textColor = color
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
textColor = value.color
|
||||
return
|
||||
}
|
||||
|
||||
textColor = value.color.withAlphaComponent(alpha)
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
textColor = theme.colors[value]
|
||||
return
|
||||
}
|
||||
|
||||
textColor = theme.colors[value]?.withAlphaComponent(alpha)
|
||||
|
||||
case .none: textColor = nil
|
||||
}
|
||||
}
|
||||
get { return self.textColor.map { .color($0) } }
|
||||
}
|
||||
}
|
||||
|
||||
public extension UITextView {
|
||||
|
@ -36,6 +166,32 @@ public extension UITextView {
|
|||
set { ThemeManager.set(self, keyPath: \.textColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
|
||||
var themeTextColorForced: ForcedThemeValue? {
|
||||
set {
|
||||
switch newValue {
|
||||
case .color(let color): textColor = color
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
textColor = value.color
|
||||
return
|
||||
}
|
||||
|
||||
textColor = value.color.withAlphaComponent(alpha)
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
textColor = theme.colors[value]
|
||||
return
|
||||
}
|
||||
|
||||
textColor = theme.colors[value]?.withAlphaComponent(alpha)
|
||||
|
||||
case .none: textColor = nil
|
||||
}
|
||||
}
|
||||
get { return self.textColor.map { .color($0) } }
|
||||
}
|
||||
}
|
||||
|
||||
public extension UITextField {
|
||||
|
@ -43,6 +199,32 @@ public extension UITextField {
|
|||
set { ThemeManager.set(self, keyPath: \.textColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
|
||||
var themeTextColorForced: ForcedThemeValue? {
|
||||
set {
|
||||
switch newValue {
|
||||
case .color(let color): textColor = color
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
textColor = value.color
|
||||
return
|
||||
}
|
||||
|
||||
textColor = value.color.withAlphaComponent(alpha)
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
textColor = theme.colors[value]
|
||||
return
|
||||
}
|
||||
|
||||
textColor = theme.colors[value]?.withAlphaComponent(alpha)
|
||||
|
||||
case .none: textColor = nil
|
||||
}
|
||||
}
|
||||
get { return self.textColor.map { .color($0) } }
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIButton {
|
||||
|
@ -71,6 +253,29 @@ public extension UIButton {
|
|||
)
|
||||
}
|
||||
|
||||
func setThemeBackgroundColorForced(_ newValue: ForcedThemeValue?, for state: UIControl.State) {
|
||||
switch newValue {
|
||||
case .color(let color): self.setBackgroundImage(color.toImage(), for: state)
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
self.setBackgroundImage(value.color.toImage(), for: state)
|
||||
return
|
||||
}
|
||||
|
||||
self.setBackgroundImage(value.color.withAlphaComponent(alpha).toImage(), for: state)
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
self.setBackgroundImage(theme.colors[value]?.toImage(), for: state)
|
||||
return
|
||||
}
|
||||
|
||||
self.setBackgroundImage(theme.colors[value]?.withAlphaComponent(alpha).toImage(), for: state)
|
||||
|
||||
case .none: self.setBackgroundImage(nil, for: state)
|
||||
}
|
||||
}
|
||||
|
||||
func setThemeTitleColor(_ value: ThemeValue?, for state: UIControl.State) {
|
||||
let keyPath: KeyPath<UIButton, UIColor?> = \.titleLabel?.textColor
|
||||
|
||||
|
@ -95,6 +300,29 @@ public extension UIButton {
|
|||
}
|
||||
)
|
||||
}
|
||||
|
||||
func setThemeTitleColorForced(_ newValue: ForcedThemeValue?, for state: UIControl.State) {
|
||||
switch newValue {
|
||||
case .color(let color): self.setTitleColor(color, for: state)
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
self.setTitleColor(value.color, for: state)
|
||||
return
|
||||
}
|
||||
|
||||
self.setTitleColor(value.color.withAlphaComponent(alpha), for: state)
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
self.setTitleColor(theme.colors[value], for: state)
|
||||
return
|
||||
}
|
||||
|
||||
self.setTitleColor(theme.colors[value]?.withAlphaComponent(alpha), for: state)
|
||||
|
||||
case .none: self.setTitleColor(nil, for: state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension UISwitch {
|
||||
|
@ -117,12 +345,57 @@ public extension UIProgressView {
|
|||
get { return nil }
|
||||
}
|
||||
|
||||
var themeProgressTintColorForced: ForcedThemeValue? {
|
||||
set {
|
||||
switch newValue {
|
||||
case .color(let color): progressTintColor = color
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
progressTintColor = value.color
|
||||
return
|
||||
}
|
||||
|
||||
progressTintColor = value.color.withAlphaComponent(alpha)
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
progressTintColor = theme.colors[value]
|
||||
return
|
||||
}
|
||||
|
||||
progressTintColor = theme.colors[value]?.withAlphaComponent(alpha)
|
||||
|
||||
case .none: progressTintColor = nil
|
||||
}
|
||||
}
|
||||
get { return self.progressTintColor.map { .color($0) } }
|
||||
}
|
||||
|
||||
var themeTrackTintColor: ThemeValue? {
|
||||
set { ThemeManager.set(self, keyPath: \.trackTintColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
}
|
||||
|
||||
public extension UISlider {
|
||||
var themeMinimumTrackTintColor: ThemeValue? {
|
||||
set { ThemeManager.set(self, keyPath: \.minimumTrackTintColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
|
||||
var themeMaximumTrackTintColor: ThemeValue? {
|
||||
set { ThemeManager.set(self, keyPath: \.maximumTrackTintColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIToolbar {
|
||||
var themeBarTintColor: ThemeValue? {
|
||||
set { ThemeManager.set(self, keyPath: \.barTintColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIContextualAction {
|
||||
var themeBackgroundColor: ThemeValue? {
|
||||
set { ThemeManager.set(self, keyPath: \.backgroundColor, to: newValue) }
|
||||
|
@ -136,13 +409,96 @@ public extension CAShapeLayer {
|
|||
get { return nil }
|
||||
}
|
||||
|
||||
var themeStrokeColorForced: ForcedThemeValue? {
|
||||
set {
|
||||
switch newValue {
|
||||
case .color(let color): strokeColor = color.cgColor
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
strokeColor = value.color.cgColor
|
||||
return
|
||||
}
|
||||
|
||||
strokeColor = value.color.withAlphaComponent(alpha).cgColor
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
strokeColor = theme.colors[value]?.cgColor
|
||||
return
|
||||
}
|
||||
|
||||
strokeColor = theme.colors[value]?.withAlphaComponent(alpha).cgColor
|
||||
|
||||
case .none: strokeColor = nil
|
||||
}
|
||||
}
|
||||
get { return self.strokeColor.map { .color(UIColor(cgColor: $0)) } }
|
||||
}
|
||||
|
||||
var themeFillColor: ThemeValue? {
|
||||
set { ThemeManager.set(self, keyPath: \.fillColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
|
||||
var themeFillColorForced: ForcedThemeValue? {
|
||||
set {
|
||||
switch newValue {
|
||||
case .color(let color): fillColor = color.cgColor
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
fillColor = value.color.cgColor
|
||||
return
|
||||
}
|
||||
|
||||
fillColor = value.color.withAlphaComponent(alpha).cgColor
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
fillColor = theme.colors[value]?.cgColor
|
||||
return
|
||||
}
|
||||
|
||||
fillColor = theme.colors[value]?.withAlphaComponent(alpha).cgColor
|
||||
|
||||
case .none: fillColor = nil
|
||||
}
|
||||
}
|
||||
get { return self.fillColor.map { .color(UIColor(cgColor: $0)) } }
|
||||
}
|
||||
}
|
||||
|
||||
public extension CALayer {
|
||||
var themeBackgroundColor: ThemeValue? {
|
||||
set { ThemeManager.set(self, keyPath: \.backgroundColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
|
||||
var themeBackgroundColorForced: ForcedThemeValue? {
|
||||
set {
|
||||
switch newValue {
|
||||
case .color(let color): backgroundColor = color.cgColor
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
backgroundColor = value.color.cgColor
|
||||
return
|
||||
}
|
||||
|
||||
backgroundColor = value.color.withAlphaComponent(alpha).cgColor
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
backgroundColor = theme.colors[value]?.cgColor
|
||||
return
|
||||
}
|
||||
|
||||
backgroundColor = theme.colors[value]?.withAlphaComponent(alpha).cgColor
|
||||
|
||||
case .none: backgroundColor = nil
|
||||
}
|
||||
}
|
||||
get { return self.backgroundColor.map { .color(UIColor(cgColor: $0)) } }
|
||||
}
|
||||
|
||||
var themeBorderColor: ThemeValue? {
|
||||
set { ThemeManager.set(self, keyPath: \.borderColor, to: newValue) }
|
||||
get { return nil }
|
||||
|
@ -153,3 +509,42 @@ public extension CALayer {
|
|||
get { return nil }
|
||||
}
|
||||
}
|
||||
|
||||
public extension CATextLayer {
|
||||
var themeForegroundColor: ThemeValue? {
|
||||
set { ThemeManager.set(self, keyPath: \.foregroundColor, to: newValue) }
|
||||
get { return nil }
|
||||
}
|
||||
|
||||
var themeForegroundColorForced: ForcedThemeValue? {
|
||||
set {
|
||||
switch newValue {
|
||||
case .color(let color): foregroundColor = color.cgColor
|
||||
case .primary(let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
foregroundColor = value.color.cgColor
|
||||
return
|
||||
}
|
||||
|
||||
foregroundColor = value.color.withAlphaComponent(alpha).cgColor
|
||||
|
||||
case .theme(let theme, let value, let alpha):
|
||||
guard let alpha: CGFloat = alpha else {
|
||||
foregroundColor = theme.colors[value]?.cgColor
|
||||
return
|
||||
}
|
||||
|
||||
foregroundColor = theme.colors[value]?.withAlphaComponent(alpha).cgColor
|
||||
|
||||
case .none: foregroundColor = nil
|
||||
}
|
||||
}
|
||||
get { return self.foregroundColor.map { .color(UIColor(cgColor: $0)) } }
|
||||
}
|
||||
}
|
||||
|
||||
public extension NSMutableAttributedString {
|
||||
func addThemeAttribute(_ attribute: ForcedThemeAttribute, range: NSRange) {
|
||||
self.addAttribute(attribute.key, value: attribute.value, range: range)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIImage {
|
||||
|
||||
func withTint(_ color: UIColor) -> UIImage? {
|
||||
let template = self.withRenderingMode(.alwaysTemplate)
|
||||
let imageView = UIImageView(image: template)
|
||||
imageView.tintColor = color
|
||||
imageView.themeTintColorForced = .color(color)
|
||||
|
||||
return imageView.toImage(isOpaque: imageView.isOpaque, scale: UIScreen.main.scale)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,22 +111,12 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value);
|
|||
|
||||
- (NSArray<NSLayoutConstraint *> *)autoPinToEdgesOfView:(UIView *)view;
|
||||
|
||||
- (void)traverseViewHierarchyWithVisitor:(UIViewVisitorBlock)visitor;
|
||||
|
||||
#pragma mark - Containers
|
||||
|
||||
+ (UIView *)containerView;
|
||||
|
||||
+ (UIView *)verticalStackWithSubviews:(NSArray<UIView *> *)subviews spacing:(int)spacing;
|
||||
|
||||
#pragma mark - Debugging
|
||||
|
||||
- (void)addBorderWithColor:(UIColor *)color;
|
||||
- (void)addRedBorder;
|
||||
|
||||
// Add red border to self, and all subviews recursively.
|
||||
- (void)addRedBorderRecursively;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
|
|
@ -439,36 +439,6 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
|||
return container;
|
||||
}
|
||||
|
||||
#pragma mark - Debugging
|
||||
|
||||
- (void)addBorderWithColor:(UIColor *)color
|
||||
{
|
||||
self.layer.borderColor = color.CGColor;
|
||||
self.layer.borderWidth = 1;
|
||||
}
|
||||
|
||||
- (void)addRedBorder
|
||||
{
|
||||
[self addBorderWithColor:[UIColor redColor]];
|
||||
}
|
||||
|
||||
- (void)addRedBorderRecursively
|
||||
{
|
||||
[self addRedBorder];
|
||||
for (UIView *subview in self.subviews) {
|
||||
[subview addRedBorderRecursively];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)traverseViewHierarchyWithVisitor:(UIViewVisitorBlock)visitor
|
||||
{
|
||||
visitor(self);
|
||||
|
||||
for (UIView *subview in self.subviews) {
|
||||
[subview traverseViewHierarchyWithVisitor:visitor];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
|
|
@ -14,6 +14,6 @@ public enum Configuration {
|
|||
|
||||
SNMessagingKit.configure()
|
||||
SNSnodeKit.configure()
|
||||
SUIKit.configure()
|
||||
SNUIKit.configure()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
@ -55,17 +53,17 @@ class AttachmentApprovalInputAccessoryView: UIView {
|
|||
// sizing when used as an input accessory view.
|
||||
self.autoresizingMask = .flexibleHeight
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.backgroundColor = .clear
|
||||
self.themeBackgroundColor = .clear
|
||||
|
||||
preservesSuperviewLayoutMargins = true
|
||||
|
||||
// Use a background view that extends below the keyboard to avoid animation glitches.
|
||||
let backgroundView = UIView()
|
||||
backgroundView.backgroundColor = UIColor.black.withAlphaComponent(0.6)
|
||||
backgroundView.themeBackgroundColor = .backgroundPrimary
|
||||
addSubview(backgroundView)
|
||||
backgroundView.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
currentCaptionLabel.textColor = .white
|
||||
currentCaptionLabel.themeTextColor = .white
|
||||
currentCaptionLabel.font = .systemFont(ofSize: Values.mediumFontSize)
|
||||
currentCaptionLabel.numberOfLines = 5
|
||||
currentCaptionLabel.lineBreakMode = .byWordWrapping
|
||||
|
@ -90,7 +88,7 @@ class AttachmentApprovalInputAccessoryView: UIView {
|
|||
stackView.autoPinEdge(toSuperviewMargin: .bottom)
|
||||
|
||||
let galleryRailBlockingView: UIView = UIView()
|
||||
galleryRailBlockingView.backgroundColor = backgroundView.backgroundColor
|
||||
galleryRailBlockingView.themeBackgroundColor = .backgroundPrimary
|
||||
stackView.addSubview(galleryRailBlockingView)
|
||||
galleryRailBlockingView.pin(.top, to: .bottom, of: attachmentTextToolbar)
|
||||
galleryRailBlockingView.pin(.left, to: .left, of: stackView)
|
||||
|
@ -101,9 +99,8 @@ class AttachmentApprovalInputAccessoryView: UIView {
|
|||
// MARK: - Events
|
||||
|
||||
@objc func captionTapped(sender: UIGestureRecognizer) {
|
||||
guard sender.state == .recognized else {
|
||||
return
|
||||
}
|
||||
guard sender.state == .recognized else { return }
|
||||
|
||||
delegate?.attachmentApprovalInputStartEditingCaptions()
|
||||
}
|
||||
|
||||
|
|
|
@ -213,18 +213,8 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC
|
|||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.view.backgroundColor = Colors.navigationBarBackground
|
||||
self.view.themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
let backgroundImage: UIImage = UIImage(color: Colors.navigationBarBackground)
|
||||
self.navigationItem.title = nil
|
||||
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
|
||||
self.navigationController?.navigationBar.shadowImage = UIImage()
|
||||
self.navigationController?.navigationBar.isTranslucent = false
|
||||
self.navigationController?.navigationBar.barTintColor = Colors.navigationBarBackground
|
||||
(self.navigationController?.navigationBar as? OWSNavigationBar)?.respectsTheme = true
|
||||
self.navigationController?.navigationBar.backgroundColor = Colors.navigationBarBackground
|
||||
self.navigationController?.navigationBar.setBackgroundImage(backgroundImage, for: .default)
|
||||
|
||||
// Avoid an unpleasant "bounce" which doesn't make sense in the context of a single item.
|
||||
pagerScrollView?.isScrollEnabled = (attachmentItems.count > 1)
|
||||
|
||||
|
@ -369,12 +359,9 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC
|
|||
let cancelButton = OWSButton(title: CommonStrings.cancelButton) { [weak self] in
|
||||
self?.cancelPressed()
|
||||
}
|
||||
cancelButton.setTitleColor(Colors.text, for: .normal)
|
||||
if let titleLabel = cancelButton.titleLabel {
|
||||
titleLabel.font = UIFont.systemFont(ofSize: 17.0)
|
||||
} else {
|
||||
owsFailDebug("Missing titleLabel.")
|
||||
}
|
||||
cancelButton.titleLabel?.font = .systemFont(ofSize: 17.0)
|
||||
cancelButton.setThemeTitleColor(.textPrimary, for: .normal)
|
||||
cancelButton.setThemeTitleColor(.textSecondary, for: .highlighted)
|
||||
cancelButton.sizeToFit()
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: cancelButton)
|
||||
}
|
||||
|
@ -382,7 +369,7 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC
|
|||
// Mimic a conventional back button, but with a shadow.
|
||||
let isRTL = CurrentAppContext().isRTL
|
||||
let imageName = (isRTL ? "NavBarBackRTL" : "NavBarBack")
|
||||
let backButton = OWSButton(imageName: imageName, tintColor: Colors.text) { [weak self] in
|
||||
let backButton = OWSButton(imageName: imageName, tintColor: .textPrimary) { [weak self] in
|
||||
self?.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
|
||||
|
@ -727,13 +714,9 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC
|
|||
// MARK: -
|
||||
|
||||
extension AttachmentApprovalViewController: AttachmentTextToolbarDelegate {
|
||||
func attachmentTextToolbarDidBeginEditing(_ attachmentTextToolbar: AttachmentTextToolbar) {
|
||||
currentPageViewController?.setAttachmentViewScale(.compact, animated: true)
|
||||
}
|
||||
func attachmentTextToolbarDidBeginEditing(_ attachmentTextToolbar: AttachmentTextToolbar) {}
|
||||
|
||||
func attachmentTextToolbarDidEndEditing(_ attachmentTextToolbar: AttachmentTextToolbar) {
|
||||
currentPageViewController?.setAttachmentViewScale(.fullsize, animated: true)
|
||||
}
|
||||
func attachmentTextToolbarDidEndEditing(_ attachmentTextToolbar: AttachmentTextToolbar) {}
|
||||
|
||||
func attachmentTextToolbarDidTapSend(_ attachmentTextToolbar: AttachmentTextToolbar) {
|
||||
// Toolbar flickers in and out if there are errors
|
||||
|
@ -768,9 +751,9 @@ extension AttachmentApprovalViewController: AttachmentPrepViewControllerDelegate
|
|||
extension SignalAttachmentItem: GalleryRailItem {
|
||||
func buildRailItemView() -> UIView {
|
||||
let imageView = UIImageView()
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
imageView.backgroundColor = UIColor.black.withAlphaComponent(0.33)
|
||||
imageView.image = getThumbnailImage()
|
||||
imageView.themeBackgroundColor = .backgroundSecondary
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
|
||||
return imageView
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import Foundation
|
|||
import UIKit
|
||||
import SessionUIKit
|
||||
|
||||
protocol AttachmentCaptionToolbarDelegate: class {
|
||||
protocol AttachmentCaptionToolbarDelegate: AnyObject {
|
||||
func attachmentCaptionToolbarDidEdit(_ attachmentCaptionToolbar: AttachmentCaptionToolbar)
|
||||
func attachmentCaptionToolbarDidComplete()
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ class AttachmentCaptionToolbar: UIView, UITextViewDelegate {
|
|||
// sizing when used as an input accessory view.
|
||||
self.autoresizingMask = .flexibleHeight
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.backgroundColor = UIColor.clear
|
||||
self.themeBackgroundColor = .clear
|
||||
|
||||
textView.delegate = self
|
||||
|
||||
|
@ -92,12 +92,12 @@ class AttachmentCaptionToolbar: UIView, UITextViewDelegate {
|
|||
let lengthLimitLabel = UILabel()
|
||||
|
||||
// Length Limit Label shown when the user inputs too long of a message
|
||||
lengthLimitLabel.textColor = .white
|
||||
lengthLimitLabel.themeTextColor = .textPrimary
|
||||
lengthLimitLabel.text = NSLocalizedString("ATTACHMENT_APPROVAL_MESSAGE_LENGTH_LIMIT_REACHED", comment: "One-line label indicating the user can add no more text to the media message field.")
|
||||
lengthLimitLabel.textAlignment = .center
|
||||
|
||||
// Add shadow in case overlayed on white content
|
||||
lengthLimitLabel.layer.shadowColor = UIColor.black.cgColor
|
||||
lengthLimitLabel.themeShadowColor = .black
|
||||
lengthLimitLabel.layer.shadowOffset = .zero
|
||||
lengthLimitLabel.layer.shadowOpacity = 0.8
|
||||
lengthLimitLabel.layer.shadowRadius = 2.0
|
||||
|
@ -127,11 +127,11 @@ class AttachmentCaptionToolbar: UIView, UITextViewDelegate {
|
|||
let textView = AttachmentTextView()
|
||||
|
||||
textView.keyboardAppearance = isLightMode ? .default : .dark
|
||||
textView.backgroundColor = .clear
|
||||
textView.tintColor = .white
|
||||
textView.themeBackgroundColor = .clear
|
||||
textView.themeTintColor = .textPrimary
|
||||
|
||||
textView.font = UIFont.ows_dynamicTypeBody
|
||||
textView.textColor = .white
|
||||
textView.themeTextColor = .textPrimary
|
||||
textView.textContainerInset = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7)
|
||||
|
||||
return textView
|
||||
|
|
|
@ -1,316 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
|
||||
protocol AttachmentCaptionDelegate: class {
|
||||
func captionView(_ captionView: AttachmentCaptionViewController, didChangeCaptionText captionText: String?, attachmentItem: SignalAttachmentItem)
|
||||
func captionViewDidCancel()
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
class AttachmentCaptionViewController: OWSViewController {
|
||||
|
||||
weak var delegate: AttachmentCaptionDelegate?
|
||||
|
||||
private let attachmentItem: SignalAttachmentItem
|
||||
|
||||
private let originalCaptionText: String?
|
||||
|
||||
private let textView = UITextView()
|
||||
|
||||
private var textViewHeightConstraint: NSLayoutConstraint?
|
||||
|
||||
private let kMaxCaptionCharacterCount = 240
|
||||
|
||||
init(delegate: AttachmentCaptionDelegate,
|
||||
attachmentItem: SignalAttachmentItem) {
|
||||
self.delegate = delegate
|
||||
self.attachmentItem = attachmentItem
|
||||
self.originalCaptionText = attachmentItem.captionText
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
self.addObserver(textView, forKeyPath: "contentSize", options: .new, context: nil)
|
||||
}
|
||||
|
||||
@available(*, unavailable, message: "use other init() instead.")
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.removeObserver(textView, forKeyPath: "contentSize")
|
||||
}
|
||||
|
||||
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
|
||||
updateTextView()
|
||||
}
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
public override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
textView.becomeFirstResponder()
|
||||
|
||||
updateTextView()
|
||||
}
|
||||
|
||||
public override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
textView.becomeFirstResponder()
|
||||
|
||||
updateTextView()
|
||||
}
|
||||
|
||||
public override func loadView() {
|
||||
self.view = UIView()
|
||||
self.view.backgroundColor = UIColor(white: 0, alpha: 0.25)
|
||||
self.view.isOpaque = false
|
||||
|
||||
self.view.isUserInteractionEnabled = true
|
||||
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(backgroundTapped)))
|
||||
|
||||
configureTextView()
|
||||
|
||||
let doneIcon = UIImage(named: "image_editor_checkmark_full")?.withRenderingMode(.alwaysTemplate)
|
||||
let doneButton = UIBarButtonItem(image: doneIcon, style: .plain,
|
||||
target: self,
|
||||
action: #selector(didTapDone))
|
||||
doneButton.tintColor = .white
|
||||
navigationItem.rightBarButtonItem = doneButton
|
||||
|
||||
self.view.layoutMargins = .zero
|
||||
|
||||
lengthLimitLabel.setContentHuggingHigh()
|
||||
lengthLimitLabel.setCompressionResistanceHigh()
|
||||
|
||||
let stackView = UIStackView(arrangedSubviews: [lengthLimitLabel, textView])
|
||||
stackView.axis = .vertical
|
||||
stackView.spacing = 20
|
||||
stackView.alignment = .fill
|
||||
stackView.layoutMargins = UIEdgeInsets(top: 16, left: 20, bottom: 16, right: 20)
|
||||
stackView.isLayoutMarginsRelativeArrangement = true
|
||||
self.view.addSubview(stackView)
|
||||
stackView.autoPinEdge(toSuperviewEdge: .leading)
|
||||
stackView.autoPinEdge(toSuperviewEdge: .trailing)
|
||||
self.autoPinView(toBottomOfViewControllerOrKeyboard: stackView, avoidNotch: true)
|
||||
|
||||
let backgroundView = UIView()
|
||||
backgroundView.backgroundColor = UIColor(white: 0, alpha: 0.5)
|
||||
view.addSubview(backgroundView)
|
||||
view.sendSubviewToBack(backgroundView)
|
||||
backgroundView.autoPinEdge(toSuperviewEdge: .leading)
|
||||
backgroundView.autoPinEdge(toSuperviewEdge: .trailing)
|
||||
backgroundView.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
backgroundView.autoPinEdge(.top, to: .top, of: stackView)
|
||||
|
||||
let minTextHeight: CGFloat = textView.font?.lineHeight ?? 0
|
||||
textViewHeightConstraint = textView.autoSetDimension(.height, toSize: minTextHeight)
|
||||
|
||||
view.addSubview(placeholderTextView)
|
||||
placeholderTextView.autoAlignAxis(.horizontal, toSameAxisOf: textView)
|
||||
placeholderTextView.autoPinEdge(.leading, to: .leading, of: textView)
|
||||
placeholderTextView.autoPinEdge(.trailing, to: .trailing, of: textView)
|
||||
}
|
||||
|
||||
private func configureTextView() {
|
||||
textView.delegate = self
|
||||
|
||||
textView.text = attachmentItem.captionText
|
||||
textView.font = UIFont.ows_dynamicTypeBody
|
||||
textView.textColor = .white
|
||||
|
||||
textView.isEditable = true
|
||||
textView.backgroundColor = .clear
|
||||
textView.isOpaque = false
|
||||
// We use a white cursor since we use a dark background.
|
||||
textView.tintColor = .white
|
||||
textView.isScrollEnabled = true
|
||||
textView.scrollsToTop = false
|
||||
textView.isUserInteractionEnabled = true
|
||||
textView.textAlignment = .left
|
||||
textView.textContainerInset = .zero
|
||||
textView.textContainer.lineFragmentPadding = 0
|
||||
textView.contentInset = .zero
|
||||
}
|
||||
|
||||
// MARK: - Events
|
||||
|
||||
@objc func backgroundTapped(sender: UIGestureRecognizer) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
completeAndDismiss(didCancel: false)
|
||||
}
|
||||
|
||||
@objc public func didTapCancel() {
|
||||
completeAndDismiss(didCancel: true)
|
||||
}
|
||||
|
||||
@objc public func didTapDone() {
|
||||
completeAndDismiss(didCancel: false)
|
||||
}
|
||||
|
||||
private func completeAndDismiss(didCancel: Bool) {
|
||||
if didCancel {
|
||||
self.delegate?.captionViewDidCancel()
|
||||
} else {
|
||||
self.delegate?.captionView(self, didChangeCaptionText: self.textView.text, attachmentItem: attachmentItem)
|
||||
}
|
||||
|
||||
self.dismiss(animated: true) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Length Limit
|
||||
|
||||
private lazy var lengthLimitLabel: UILabel = {
|
||||
let lengthLimitLabel = UILabel()
|
||||
|
||||
// Length Limit Label shown when the user inputs too long of a message
|
||||
lengthLimitLabel.textColor = UIColor.ows_destructiveRed
|
||||
lengthLimitLabel.text = NSLocalizedString("ATTACHMENT_APPROVAL_CAPTION_LENGTH_LIMIT_REACHED", comment: "One-line label indicating the user can add no more text to the attachment caption.")
|
||||
lengthLimitLabel.textAlignment = .center
|
||||
|
||||
// Add shadow in case overlayed on white content
|
||||
lengthLimitLabel.layer.shadowColor = UIColor.black.cgColor
|
||||
lengthLimitLabel.layer.shadowOffset = .zero
|
||||
lengthLimitLabel.layer.shadowOpacity = 0.8
|
||||
lengthLimitLabel.isHidden = true
|
||||
|
||||
return lengthLimitLabel
|
||||
}()
|
||||
|
||||
// MARK: - Text Height
|
||||
|
||||
// TODO: We need to revisit this with Myles.
|
||||
func updatePlaceholderTextViewVisibility() {
|
||||
let isHidden: Bool = {
|
||||
guard !self.textView.isFirstResponder else {
|
||||
return true
|
||||
}
|
||||
|
||||
guard let captionText = self.textView.text else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard captionText.count > 0 else {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}()
|
||||
|
||||
placeholderTextView.isHidden = isHidden
|
||||
}
|
||||
|
||||
private lazy var placeholderTextView: UIView = {
|
||||
let placeholderTextView = UITextView()
|
||||
placeholderTextView.text = NSLocalizedString("ATTACHMENT_APPROVAL_CAPTION_PLACEHOLDER", comment: "placeholder text for an empty captioning field")
|
||||
placeholderTextView.isEditable = false
|
||||
|
||||
placeholderTextView.backgroundColor = .clear
|
||||
placeholderTextView.font = UIFont.ows_dynamicTypeBody
|
||||
|
||||
placeholderTextView.textColor = Colors.text
|
||||
placeholderTextView.tintColor = Colors.text
|
||||
placeholderTextView.returnKeyType = .done
|
||||
|
||||
return placeholderTextView
|
||||
}()
|
||||
|
||||
// MARK: - Text Height
|
||||
|
||||
private func updateTextView() {
|
||||
guard let textViewHeightConstraint = textViewHeightConstraint else {
|
||||
owsFailDebug("Missing textViewHeightConstraint.")
|
||||
return
|
||||
}
|
||||
|
||||
let contentSize = textView.sizeThatFits(CGSize(width: textView.width(), height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
// `textView.contentSize` isn't accurate when restoring a multiline draft, so we compute it here.
|
||||
textView.contentSize = contentSize
|
||||
|
||||
let minHeight: CGFloat = textView.font?.lineHeight ?? 0
|
||||
let maxHeight: CGFloat = 300
|
||||
let newHeight = contentSize.height.clamp(minHeight, maxHeight)
|
||||
|
||||
textViewHeightConstraint.constant = newHeight
|
||||
textView.invalidateIntrinsicContentSize()
|
||||
textView.superview?.invalidateIntrinsicContentSize()
|
||||
|
||||
textView.isScrollEnabled = contentSize.height > maxHeight
|
||||
|
||||
updatePlaceholderTextViewVisibility()
|
||||
}
|
||||
}
|
||||
|
||||
extension AttachmentCaptionViewController: UITextViewDelegate {
|
||||
|
||||
public func textViewDidChange(_ textView: UITextView) {
|
||||
updateTextView()
|
||||
}
|
||||
|
||||
public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
let existingText: String = textView.text ?? ""
|
||||
let proposedText: String = (existingText as NSString).replacingCharacters(in: range, with: text)
|
||||
|
||||
let kMaxCaptionByteCount = kOversizeTextMessageSizeThreshold / 4
|
||||
guard proposedText.utf8.count <= kMaxCaptionByteCount else {
|
||||
Logger.debug("hit caption byte count limit")
|
||||
self.lengthLimitLabel.isHidden = false
|
||||
|
||||
// `range` represents the section of the existing text we will replace. We can re-use that space.
|
||||
// Range is in units of NSStrings's standard UTF-16 characters. Since some of those chars could be
|
||||
// represented as single bytes in utf-8, while others may be 8 or more, the only way to be sure is
|
||||
// to just measure the utf8 encoded bytes of the replaced substring.
|
||||
let bytesAfterDelete: Int = (existingText as NSString).replacingCharacters(in: range, with: "").utf8.count
|
||||
|
||||
// Accept as much of the input as we can
|
||||
let byteBudget: Int = Int(kOversizeTextMessageSizeThreshold) - bytesAfterDelete
|
||||
if byteBudget >= 0, let acceptableNewText = text.truncated(toByteCount: UInt(byteBudget)) {
|
||||
textView.text = (existingText as NSString).replacingCharacters(in: range, with: acceptableNewText)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// After verifying the byte-length is sufficiently small, verify the character count is within bounds.
|
||||
// Normally this character count should entail *much* less byte count.
|
||||
guard proposedText.count <= kMaxCaptionCharacterCount else {
|
||||
Logger.debug("hit caption character count limit")
|
||||
|
||||
self.lengthLimitLabel.isHidden = false
|
||||
|
||||
// `range` represents the section of the existing text we will replace. We can re-use that space.
|
||||
let charsAfterDelete: Int = (existingText as NSString).replacingCharacters(in: range, with: "").count
|
||||
|
||||
// Accept as much of the input as we can
|
||||
let charBudget: Int = Int(kMaxCaptionCharacterCount) - charsAfterDelete
|
||||
if charBudget >= 0 {
|
||||
let acceptableNewText = String(text.prefix(charBudget))
|
||||
textView.text = (existingText as NSString).replacingCharacters(in: range, with: acceptableNewText)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
self.lengthLimitLabel.isHidden = true
|
||||
return true
|
||||
}
|
||||
|
||||
public func textViewDidBeginEditing(_ textView: UITextView) {
|
||||
updatePlaceholderTextViewVisibility()
|
||||
}
|
||||
|
||||
public func textViewDidEndEditing(_ textView: UITextView) {
|
||||
updatePlaceholderTextViewVisibility()
|
||||
}
|
||||
}
|
|
@ -9,10 +9,10 @@ import SessionMessagingKit
|
|||
class AddMoreRailItem: GalleryRailItem {
|
||||
func buildRailItemView() -> UIView {
|
||||
let view = UIView()
|
||||
view.backgroundColor = UIColor.black.withAlphaComponent(0.33)
|
||||
view.themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
let iconView = UIImageView(image: #imageLiteral(resourceName: "ic_plus_24").withRenderingMode(.alwaysTemplate))
|
||||
iconView.tintColor = .ows_white
|
||||
iconView.themeTintColor = .textPrimary
|
||||
view.addSubview(iconView)
|
||||
iconView.setCompressionResistanceHigh()
|
||||
iconView.setContentHuggingHigh()
|
||||
|
@ -66,9 +66,9 @@ class SignalAttachmentItem: Hashable {
|
|||
}
|
||||
|
||||
// MARK: Hashable
|
||||
|
||||
public var hashValue: Int {
|
||||
return attachment.hashValue
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
attachment.hash(into: &hasher)
|
||||
}
|
||||
|
||||
// MARK: Equatable
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
@ -49,7 +47,7 @@ public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarD
|
|||
AttachmentTextToolbar.kMinTextViewHeight + (AttachmentTextToolbar.kToolbarMargin * 2)
|
||||
)
|
||||
|
||||
private lazy var scrollView: UIScrollView = {
|
||||
public lazy var scrollView: UIScrollView = {
|
||||
// Scroll View - used to zoom/pan on images and video
|
||||
let scrollView: UIScrollView = UIScrollView()
|
||||
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -124,9 +122,8 @@ public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarD
|
|||
}()
|
||||
|
||||
public var shouldHideControls: Bool {
|
||||
guard let imageEditorView = imageEditorView else {
|
||||
return false
|
||||
}
|
||||
guard let imageEditorView = imageEditorView else { return false }
|
||||
|
||||
return imageEditorView.shouldHideControls
|
||||
}
|
||||
|
||||
|
@ -134,7 +131,9 @@ public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarD
|
|||
|
||||
init(attachmentItem: SignalAttachmentItem) {
|
||||
self.attachmentItem = attachmentItem
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
if attachment.hasError {
|
||||
owsFailDebug(attachment.error.debugDescription)
|
||||
}
|
||||
|
@ -149,13 +148,16 @@ public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarD
|
|||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.backgroundColor = Colors.navigationBarBackground
|
||||
view.themeBackgroundColor = .backgroundSecondary
|
||||
|
||||
view.addSubview(contentContainerView)
|
||||
|
||||
contentContainerView.addSubview(scrollView)
|
||||
scrollView.addSubview(mediaMessageView)
|
||||
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(screenTapped))
|
||||
mediaMessageView.addGestureRecognizer(tapGesture)
|
||||
|
||||
if attachment.isImage, let editorView: ImageEditorView = imageEditorView {
|
||||
view.addSubview(editorView)
|
||||
|
||||
|
@ -292,9 +294,13 @@ public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarD
|
|||
}
|
||||
|
||||
// MARK: - Event Handlers
|
||||
|
||||
@objc func screenTapped() {
|
||||
self.view.window?.endEditing(true)
|
||||
}
|
||||
|
||||
@objc public func didTapPlayerView(_ gestureRecognizer: UIGestureRecognizer) {
|
||||
assert(self.videoPlayer != nil)
|
||||
self.view.window?.endEditing(true)
|
||||
self.pauseVideo()
|
||||
}
|
||||
|
||||
|
@ -527,25 +533,15 @@ extension AttachmentPrepViewController: UIScrollViewDelegate {
|
|||
// MARK: -
|
||||
|
||||
extension AttachmentPrepViewController: ImageEditorViewDelegate {
|
||||
public func imageEditor(presentFullScreenView viewController: UIViewController,
|
||||
isTransparent: Bool) {
|
||||
|
||||
public func imageEditor(presentFullScreenView viewController: UIViewController, isTransparent: Bool) {
|
||||
let navigationController = OWSNavigationController(rootViewController: viewController)
|
||||
navigationController.modalPresentationStyle = (isTransparent
|
||||
? .overFullScreen
|
||||
: .fullScreen)
|
||||
navigationController.modalPresentationStyle = (isTransparent ?
|
||||
.overFullScreen :
|
||||
.fullScreen
|
||||
)
|
||||
navigationController.ows_prefersStatusBarHidden = true
|
||||
navigationController.view.backgroundColor = Colors.navigationBarBackground
|
||||
|
||||
if let navigationBar = navigationController.navigationBar as? OWSNavigationBar {
|
||||
navigationBar.overrideTheme(type: .clear)
|
||||
} else {
|
||||
owsFailDebug("navigationBar was nil or unexpected class")
|
||||
}
|
||||
|
||||
self.present(navigationController, animated: false) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
self.present(navigationController, animated: false, completion: nil)
|
||||
}
|
||||
|
||||
public func imageEditorUpdateNavigationBar() {
|
||||
|
|
|
@ -9,7 +9,7 @@ import SessionUIKit
|
|||
// Coincides with Android's max text message length
|
||||
let kMaxMessageBodyCharacterCount = 2000
|
||||
|
||||
protocol AttachmentTextToolbarDelegate: class {
|
||||
protocol AttachmentTextToolbarDelegate: AnyObject {
|
||||
func attachmentTextToolbarDidTapSend(_ attachmentTextToolbar: AttachmentTextToolbar)
|
||||
func attachmentTextToolbarDidBeginEditing(_ attachmentTextToolbar: AttachmentTextToolbar)
|
||||
func attachmentTextToolbarDidEndEditing(_ attachmentTextToolbar: AttachmentTextToolbar)
|
||||
|
@ -55,7 +55,7 @@ class AttachmentTextToolbar: UIView, UITextViewDelegate {
|
|||
// sizing when used as an input accessory view.
|
||||
self.autoresizingMask = .flexibleHeight
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.backgroundColor = UIColor.clear
|
||||
self.themeBackgroundColor = .clear
|
||||
|
||||
textView.delegate = self
|
||||
|
||||
|
@ -65,7 +65,7 @@ class AttachmentTextToolbar: UIView, UITextViewDelegate {
|
|||
|
||||
sendButton.titleLabel?.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
||||
sendButton.titleLabel?.textAlignment = .center
|
||||
sendButton.tintColor = Colors.accent
|
||||
sendButton.themeTintColor = .primary
|
||||
|
||||
// Increase hit area of send button
|
||||
sendButton.contentEdgeInsets = UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)
|
||||
|
@ -138,12 +138,12 @@ class AttachmentTextToolbar: UIView, UITextViewDelegate {
|
|||
let lengthLimitLabel = UILabel()
|
||||
|
||||
// Length Limit Label shown when the user inputs too long of a message
|
||||
lengthLimitLabel.textColor = .white
|
||||
lengthLimitLabel.text = NSLocalizedString("ATTACHMENT_APPROVAL_MESSAGE_LENGTH_LIMIT_REACHED", comment: "One-line label indicating the user can add no more text to the media message field.")
|
||||
lengthLimitLabel.text = "ATTACHMENT_APPROVAL_MESSAGE_LENGTH_LIMIT_REACHED".localized()
|
||||
lengthLimitLabel.themeTextColor = .textPrimary
|
||||
lengthLimitLabel.textAlignment = .center
|
||||
|
||||
// Add shadow in case overlayed on white content
|
||||
lengthLimitLabel.layer.shadowColor = UIColor.black.cgColor
|
||||
lengthLimitLabel.themeShadowColor = .black
|
||||
lengthLimitLabel.layer.shadowOffset = .zero
|
||||
lengthLimitLabel.layer.shadowOpacity = 0.8
|
||||
lengthLimitLabel.layer.shadowRadius = 2.0
|
||||
|
@ -173,7 +173,7 @@ class AttachmentTextToolbar: UIView, UITextViewDelegate {
|
|||
private lazy var textContainer: UIView = {
|
||||
let textContainer = UIView()
|
||||
|
||||
textContainer.layer.borderColor = UIColor.white.cgColor
|
||||
textContainer.themeBorderColor = .borderSeparator
|
||||
textContainer.layer.borderWidth = Values.separatorThickness
|
||||
textContainer.layer.cornerRadius = (AttachmentTextToolbar.kMinTextViewHeight / 2)
|
||||
textContainer.clipsToBounds = true
|
||||
|
@ -191,11 +191,11 @@ class AttachmentTextToolbar: UIView, UITextViewDelegate {
|
|||
let textView = AttachmentTextView()
|
||||
|
||||
textView.keyboardAppearance = isLightMode ? .default : .dark
|
||||
textView.backgroundColor = .clear
|
||||
textView.tintColor = .white
|
||||
textView.themeBackgroundColor = .clear
|
||||
textView.themeTintColor = .textPrimary
|
||||
|
||||
textView.font = .systemFont(ofSize: Values.mediumFontSize)
|
||||
textView.textColor = .white
|
||||
textView.themeTextColor = .textPrimary
|
||||
textView.showsVerticalScrollIndicator = false
|
||||
textView.textContainerInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
|
||||
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
|
||||
@objc
|
||||
public protocol ImageEditorBrushViewControllerDelegate: class {
|
||||
public protocol ImageEditorBrushViewControllerDelegate: AnyObject {
|
||||
func brushDidComplete(currentColor: ImageEditorColor)
|
||||
}
|
||||
|
||||
|
@ -17,24 +15,27 @@ public class ImageEditorBrushViewController: OWSViewController {
|
|||
private weak var delegate: ImageEditorBrushViewControllerDelegate?
|
||||
|
||||
private let model: ImageEditorModel
|
||||
|
||||
private let canvasView: ImageEditorCanvasView
|
||||
|
||||
private let paletteView: ImageEditorPaletteView
|
||||
private let bottomInset: CGFloat
|
||||
|
||||
// We only want to let users undo changes made in this view.
|
||||
// So we snapshot any older "operation id" and prevent
|
||||
// users from undoing it.
|
||||
private let firstUndoOperationId: String?
|
||||
|
||||
init(delegate: ImageEditorBrushViewControllerDelegate,
|
||||
model: ImageEditorModel,
|
||||
currentColor: ImageEditorColor) {
|
||||
init(
|
||||
delegate: ImageEditorBrushViewControllerDelegate,
|
||||
model: ImageEditorModel,
|
||||
currentColor: ImageEditorColor,
|
||||
bottomInset: CGFloat
|
||||
) {
|
||||
self.delegate = delegate
|
||||
self.model = model
|
||||
self.canvasView = ImageEditorCanvasView(model: model)
|
||||
self.paletteView = ImageEditorPaletteView(currentColor: currentColor)
|
||||
self.firstUndoOperationId = model.currentUndoOperationId()
|
||||
self.bottomInset = bottomInset
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
|
@ -50,16 +51,19 @@ public class ImageEditorBrushViewController: OWSViewController {
|
|||
|
||||
public override func loadView() {
|
||||
self.view = UIView()
|
||||
self.view.backgroundColor = Colors.navigationBarBackground
|
||||
self.view.themeBackgroundColor = .backgroundSecondary
|
||||
self.view.isOpaque = true
|
||||
|
||||
canvasView.configureSubviews()
|
||||
self.view.addSubview(canvasView)
|
||||
canvasView.autoPinEdgesToSuperviewEdges()
|
||||
canvasView.pin(.top, to: .top, of: self.view)
|
||||
canvasView.pin(.leading, to: .leading, of: self.view)
|
||||
canvasView.pin(.trailing, to: .trailing, of: self.view)
|
||||
canvasView.pin(.bottom, to: .bottom, of: self.view, withInset: -bottomInset)
|
||||
|
||||
paletteView.delegate = self
|
||||
self.view.addSubview(paletteView)
|
||||
paletteView.autoVCenterInSuperview()
|
||||
paletteView.center(.vertical, in: self.view, withInset: -(bottomInset / 2))
|
||||
paletteView.autoPinEdge(toSuperviewEdge: .trailing, withInset: 0)
|
||||
|
||||
self.view.isUserInteractionEnabled = true
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
|
||||
public class EditorTextLayer: CATextLayer {
|
||||
let itemId: String
|
||||
|
@ -71,13 +70,13 @@ public class ImageEditorCanvasView: UIView {
|
|||
|
||||
@objc
|
||||
public func configureSubviews() {
|
||||
self.backgroundColor = .clear
|
||||
self.themeBackgroundColor = .clear
|
||||
self.isOpaque = false
|
||||
|
||||
self.srcImage = loadSrcImage()
|
||||
|
||||
clipView.clipsToBounds = true
|
||||
clipView.backgroundColor = .clear
|
||||
clipView.themeBackgroundColor = .clear
|
||||
clipView.isOpaque = false
|
||||
clipView.layoutCallback = { [weak self] (_) in
|
||||
guard let strongSelf = self else {
|
||||
|
@ -92,7 +91,7 @@ public class ImageEditorCanvasView: UIView {
|
|||
imageLayer.contentsScale = srcImage.scale
|
||||
}
|
||||
|
||||
contentView.backgroundColor = .clear
|
||||
contentView.themeBackgroundColor = .clear
|
||||
contentView.isOpaque = false
|
||||
contentView.layer.addSublayer(imageLayer)
|
||||
contentView.layoutCallback = { [weak self] (_) in
|
||||
|
@ -396,7 +395,7 @@ public class ImageEditorCanvasView: UIView {
|
|||
|
||||
let shapeLayer = CAShapeLayer()
|
||||
shapeLayer.lineWidth = strokeWidth
|
||||
shapeLayer.strokeColor = item.color.cgColor
|
||||
shapeLayer.themeStrokeColorForced = .color(item.color)
|
||||
shapeLayer.frame = CGRect(origin: .zero, size: viewSize)
|
||||
|
||||
// Stroke samples are specified in "image unit" coordinates, but
|
||||
|
@ -469,7 +468,7 @@ public class ImageEditorCanvasView: UIView {
|
|||
}
|
||||
|
||||
shapeLayer.path = bezierPath.cgPath
|
||||
shapeLayer.fillColor = nil
|
||||
shapeLayer.themeFillColor = nil
|
||||
shapeLayer.lineCap = CAShapeLayerLineCap.round
|
||||
shapeLayer.lineJoin = CAShapeLayerLineJoin.round
|
||||
shapeLayer.zPosition = zPositionForItem(item: item, model: model, zPositionBase: brushLayerZ)
|
||||
|
@ -501,14 +500,17 @@ public class ImageEditorCanvasView: UIView {
|
|||
let fontSize = item.font.pointSize * imageFrame.size.width / item.fontReferenceImageWidth
|
||||
|
||||
let text = item.text.filterForDisplay ?? ""
|
||||
let attributedString = NSAttributedString(string: text,
|
||||
attributes: [
|
||||
NSAttributedString.Key.font: item.font.withSize(fontSize),
|
||||
NSAttributedString.Key.foregroundColor: item.color.color
|
||||
])
|
||||
let attributedString: NSMutableAttributedString = NSMutableAttributedString(
|
||||
string: text,
|
||||
attributes: [ .font: item.font.withSize(fontSize) ]
|
||||
)
|
||||
attributedString.addThemeAttribute(
|
||||
.foreground(item.color.color),
|
||||
range: NSRange(location: 0, length: text.count)
|
||||
)
|
||||
let layer = EditorTextLayer(itemId: item.itemId)
|
||||
layer.string = attributedString
|
||||
layer.foregroundColor = item.color.cgColor
|
||||
layer.themeForegroundColorForced = .color(item.color.color)
|
||||
layer.font = CGFont(item.font.fontName as CFString)
|
||||
layer.fontSize = fontSize
|
||||
layer.isWrapped = true
|
||||
|
@ -608,14 +610,14 @@ public class ImageEditorCanvasView: UIView {
|
|||
// Because CALayer.renderInContext() doesn't honor CALayer properties like frame,
|
||||
// transform, etc.
|
||||
let view = UIView()
|
||||
view.backgroundColor = UIColor.clear
|
||||
view.themeBackgroundColor = .clear
|
||||
view.isOpaque = false
|
||||
view.frame = CGRect(origin: .zero, size: viewSize)
|
||||
|
||||
// Rendering a UIView to an image will not honor the root image's layer transform.
|
||||
// We therefore use a subview.
|
||||
let contentView = UIView()
|
||||
contentView.backgroundColor = UIColor.clear
|
||||
contentView.themeBackgroundColor = .clear
|
||||
contentView.isOpaque = false
|
||||
contentView.frame = CGRect(origin: .zero, size: viewSize)
|
||||
view.addSubview(contentView)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue