From 9a1082dd89d147fcdc8e350c0c821d1ac2fbdb58 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Wed, 11 Sep 2024 17:28:13 -0400 Subject: [PATCH] Update registration permissions onboarding design & copy --- .swiftlint.yml | 3 +- Signal.xcodeproj/project.pbxproj | 62 +++--- Signal/Colors.xcassets/Contents.json | 6 + .../Signal/Background/Contents.json | 6 + .../background.colorset/Contents.json | 38 ++++ .../Contents.json | 78 +++++++ Signal/Colors.xcassets/Signal/Contents.json | 9 + .../Signal/Label/Contents.json | 6 + .../Signal/Label/label.colorset/Contents.json | 38 ++++ .../quaternaryLabel.colorset/Contents.json | 78 +++++++ .../secondaryLabel.colorset/Contents.json | 78 +++++++ .../tertiaryLabel.colorset/Contents.json | 78 +++++++ .../bell-ring.imageset/Contents.json | 12 ++ .../bell-ring.imageset/bell-ring.pdf | Bin 0 -> 5396 bytes .../Contents.json | 12 ++ .../person-circle.pdf | Bin 0 -> 2329 bytes .../images/notificationPermission0.png | Bin 31425 -> 0 bytes .../images/notificationPermission1.png | Bin 6107 -> 0 bytes .../images/notificationPermission2.png | Bin 6030 -> 0 bytes .../notificationPermission.json | 1 - .../RegistrationCoordinatorImpl.swift | 2 +- Signal/Registration/RegistrationStep.swift | 2 +- .../RegistrationNavigationController.swift | 11 +- .../RegistrationPermissionsView.swift | 197 ++++++++++++++++++ ...egistrationPermissionsViewController.swift | 180 ---------------- ...tedAndMustCreateNewPinViewController.swift | 4 +- ...strationReglockTimeoutViewController.swift | 4 +- .../RegistrationSplashViewController.swift | 4 +- ...gistrationVerificationViewController.swift | 4 +- .../UserInterface/RegistrationViewUtil.swift | 12 +- .../RegistrationCoordinatorTest.swift | 12 +- .../translations/en.lproj/Localizable.strings | 17 +- .../AccessibleLayoutMetric.swift | 54 +++++ .../SwiftUIExtensions/AsyncViewTask.swift | 79 +++++++ .../ScrollBounceBehaviorIfAvailable.swift | 39 ++++ 35 files changed, 869 insertions(+), 257 deletions(-) create mode 100644 Signal/Colors.xcassets/Contents.json create mode 100644 Signal/Colors.xcassets/Signal/Background/Contents.json create mode 100644 Signal/Colors.xcassets/Signal/Background/background.colorset/Contents.json create mode 100644 Signal/Colors.xcassets/Signal/Background/secondaryBackground.colorset/Contents.json create mode 100644 Signal/Colors.xcassets/Signal/Contents.json create mode 100644 Signal/Colors.xcassets/Signal/Label/Contents.json create mode 100644 Signal/Colors.xcassets/Signal/Label/label.colorset/Contents.json create mode 100644 Signal/Colors.xcassets/Signal/Label/quaternaryLabel.colorset/Contents.json create mode 100644 Signal/Colors.xcassets/Signal/Label/secondaryLabel.colorset/Contents.json create mode 100644 Signal/Colors.xcassets/Signal/Label/tertiaryLabel.colorset/Contents.json create mode 100644 Signal/Images.xcassets/bell-ring.imageset/Contents.json create mode 100644 Signal/Images.xcassets/bell-ring.imageset/bell-ring.pdf create mode 100644 Signal/Images.xcassets/person-circle-large.imageset/Contents.json create mode 100644 Signal/Images.xcassets/person-circle-large.imageset/person-circle.pdf delete mode 100644 Signal/Lottie/NotificationPermission/images/notificationPermission0.png delete mode 100644 Signal/Lottie/NotificationPermission/images/notificationPermission1.png delete mode 100644 Signal/Lottie/NotificationPermission/images/notificationPermission2.png delete mode 100644 Signal/Lottie/NotificationPermission/notificationPermission.json create mode 100644 Signal/Registration/UserInterface/RegistrationPermissionsView.swift delete mode 100644 Signal/Registration/UserInterface/RegistrationPermissionsViewController.swift create mode 100644 SignalUI/SwiftUIExtensions/AccessibleLayoutMetric.swift create mode 100644 SignalUI/SwiftUIExtensions/AsyncViewTask.swift create mode 100644 SignalUI/SwiftUIExtensions/ScrollBounceBehaviorIfAvailable.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index dd5b13d3ca..6807813b82 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -40,7 +40,8 @@ opt_in_rules: - empty_string - sorted_first_last attributes: - always_on_line_above: ["@objc", "@nonobjc"] + always_on_line_above: ["@available", "@objc", "@nonobjc"] + attributes_with_arguments_always_on_line_above: false inclusive_language: override_allowed_terms: ["master", "whitelist"] large_tuple: diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 9c412e2a98..400f2004cf 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 05104D162C88EC3A00F8851F /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 05104D142C88CDB300F8851F /* Colors.xcassets */; }; + 05104D182C8A151100F8851F /* AsyncViewTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05104D172C8A151100F8851F /* AsyncViewTask.swift */; }; + 05104E3A2C8B541000F8851F /* AccessibleLayoutMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05104E392C8B540C00F8851F /* AccessibleLayoutMetric.swift */; }; + 0510F69E2C91EB3000FA3FDE /* ScrollBounceBehaviorIfAvailable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0510F69D2C91EB2800FA3FDE /* ScrollBounceBehaviorIfAvailable.swift */; }; 0512145B2C5BCECF0021EEC9 /* CollectionDifference+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0512145A2C5BCECF0021EEC9 /* CollectionDifference+SSK.swift */; }; 0517B9782BFCFF12002CDE7D /* TSThreadTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0517B9772BFCFF12002CDE7D /* TSThreadTests.swift */; }; 052647C12C6404DD0076E99D /* ChatListFilterStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 052647C02C6404D70076E99D /* ChatListFilterStore.swift */; }; @@ -523,10 +527,6 @@ 4C8A6DFC22E5499300469AE7 /* MediaZoomAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C8A6DFB22E5499300469AE7 /* MediaZoomAnimationController.swift */; }; 4C8A6DFE22E54AFA00469AE7 /* MediaInteractiveDismiss.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C8A6DFD22E54AFA00469AE7 /* MediaInteractiveDismiss.swift */; }; 4C9D347B23679C25006A4307 /* ContactStreamTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9D347923679C13006A4307 /* ContactStreamTest.swift */; }; - 4C9D34972369F0FC006A4307 /* notificationPermission.json in Resources */ = {isa = PBXBuildFile; fileRef = 4C9D34962369F0FC006A4307 /* notificationPermission.json */; }; - 4C9D349B2369F11F006A4307 /* notificationPermission1.png in Resources */ = {isa = PBXBuildFile; fileRef = 4C9D34982369F11E006A4307 /* notificationPermission1.png */; }; - 4C9D349C2369F11F006A4307 /* notificationPermission0.png in Resources */ = {isa = PBXBuildFile; fileRef = 4C9D34992369F11E006A4307 /* notificationPermission0.png */; }; - 4C9D349D2369F11F006A4307 /* notificationPermission2.png in Resources */ = {isa = PBXBuildFile; fileRef = 4C9D349A2369F11F006A4307 /* notificationPermission2.png */; }; 4CA46F4C219CCC630038ABDE /* MediaCaptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA46F4B219CCC630038ABDE /* MediaCaptionView.swift */; }; 4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA485BA2232339F004B9E7D /* PhotoCaptureViewController.swift */; }; 4CB5F26720F6E1E2004D1B42 /* MessageActionsToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF4C0920F55BBA005DA313 /* MessageActionsToolbar.swift */; }; @@ -3100,7 +3100,7 @@ F9D5BFCD2979A017001737E5 /* OWSRequestFactory+Spam.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9D5BFCC2979A017001737E5 /* OWSRequestFactory+Spam.swift */; }; F9D5BFCF2979AFF4001737E5 /* URLPathComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9D5BFCE2979AFF4001737E5 /* URLPathComponents.swift */; }; F9D5BFD12979B027001737E5 /* URLPathComponentsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9D5BFD02979B027001737E5 /* URLPathComponentsTest.swift */; }; - F9D5C39F2993F9FF004891FC /* RegistrationPermissionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9D5C39E2993F9FF004891FC /* RegistrationPermissionsViewController.swift */; }; + F9D5C39F2993F9FF004891FC /* RegistrationPermissionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9D5C39E2993F9FF004891FC /* RegistrationPermissionsView.swift */; }; F9D83012282DBB1500399363 /* BadgeGiftingChooseBadgeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9D83011282DBB1500399363 /* BadgeGiftingChooseBadgeViewController.swift */; }; F9DD70B92811AF82000C5960 /* DonationViewsUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9DD70B82811AF82000C5960 /* DonationViewsUtil.swift */; }; F9E3006129A02D8800DCA219 /* RegistrationPinViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E3006029A02D8800DCA219 /* RegistrationPinViewController.swift */; }; @@ -3246,6 +3246,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 05104D142C88CDB300F8851F /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; + 05104D172C8A151100F8851F /* AsyncViewTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncViewTask.swift; sourceTree = ""; }; + 05104E392C8B540C00F8851F /* AccessibleLayoutMetric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibleLayoutMetric.swift; sourceTree = ""; }; + 0510F69D2C91EB2800FA3FDE /* ScrollBounceBehaviorIfAvailable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollBounceBehaviorIfAvailable.swift; sourceTree = ""; }; 0512145A2C5BCECF0021EEC9 /* CollectionDifference+SSK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CollectionDifference+SSK.swift"; sourceTree = ""; }; 0517B9772BFCFF12002CDE7D /* TSThreadTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSThreadTests.swift; sourceTree = ""; }; 052647C02C6404D70076E99D /* ChatListFilterStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListFilterStore.swift; sourceTree = ""; }; @@ -3846,10 +3850,6 @@ 4C9C50FF22F495F60054A33F /* TSAttachmentMultisendJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TSAttachmentMultisendJob.swift; sourceTree = ""; }; 4C9D347923679C13006A4307 /* ContactStreamTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactStreamTest.swift; sourceTree = ""; }; 4C9D347E23689E06006A4307 /* IncomingContactSyncJobQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncomingContactSyncJobQueue.swift; sourceTree = ""; }; - 4C9D34962369F0FC006A4307 /* notificationPermission.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = notificationPermission.json; sourceTree = ""; }; - 4C9D34982369F11E006A4307 /* notificationPermission1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = notificationPermission1.png; sourceTree = ""; }; - 4C9D34992369F11E006A4307 /* notificationPermission0.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = notificationPermission0.png; sourceTree = ""; }; - 4C9D349A2369F11F006A4307 /* notificationPermission2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = notificationPermission2.png; sourceTree = ""; }; 4CA46F4B219CCC630038ABDE /* MediaCaptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaCaptionView.swift; sourceTree = ""; }; 4CA485BA2232339F004B9E7D /* PhotoCaptureViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoCaptureViewController.swift; sourceTree = ""; }; 4CB5F26820F7D060004D1B42 /* MessageActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActions.swift; sourceTree = ""; }; @@ -6502,7 +6502,7 @@ F9D5BFCC2979A017001737E5 /* OWSRequestFactory+Spam.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OWSRequestFactory+Spam.swift"; sourceTree = ""; }; F9D5BFCE2979AFF4001737E5 /* URLPathComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLPathComponents.swift; sourceTree = ""; }; F9D5BFD02979B027001737E5 /* URLPathComponentsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLPathComponentsTest.swift; sourceTree = ""; }; - F9D5C39E2993F9FF004891FC /* RegistrationPermissionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationPermissionsViewController.swift; sourceTree = ""; }; + F9D5C39E2993F9FF004891FC /* RegistrationPermissionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationPermissionsView.swift; sourceTree = ""; }; F9D83011282DBB1500399363 /* BadgeGiftingChooseBadgeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeGiftingChooseBadgeViewController.swift; sourceTree = ""; }; F9DD70B82811AF82000C5960 /* DonationViewsUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonationViewsUtil.swift; sourceTree = ""; }; F9E3006029A02D8800DCA219 /* RegistrationPinViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationPinViewController.swift; sourceTree = ""; }; @@ -7659,25 +7659,6 @@ path = MediaGallery; sourceTree = ""; }; - 4C9D34842369EA3E006A4307 /* NotificationPermission */ = { - isa = PBXGroup; - children = ( - 4C9D348B2369EA69006A4307 /* images */, - 4C9D34962369F0FC006A4307 /* notificationPermission.json */, - ); - path = NotificationPermission; - sourceTree = ""; - }; - 4C9D348B2369EA69006A4307 /* images */ = { - isa = PBXGroup; - children = ( - 4C9D34992369F11E006A4307 /* notificationPermission0.png */, - 4C9D34982369F11E006A4307 /* notificationPermission1.png */, - 4C9D349A2369F11F006A4307 /* notificationPermission2.png */, - ); - path = images; - sourceTree = ""; - }; 4CD675BF22E7BE47008010D2 /* Transitions */ = { isa = PBXGroup; children = ( @@ -8938,7 +8919,7 @@ 6659CCB029CD4650000C24C0 /* RegistrationConfirmModeSwitchViewController.swift */, F92E4C73299E9A0100C6E6C7 /* RegistrationLoadingViewController.swift */, F95A64F2299589CA007FDBDF /* RegistrationNavigationController.swift */, - F9D5C39E2993F9FF004891FC /* RegistrationPermissionsViewController.swift */, + F9D5C39E2993F9FF004891FC /* RegistrationPermissionsView.swift */, F905DFEA29A534F200BAD034 /* RegistrationPhoneNumberDiscoverabilityViewController.swift */, F9198484299AA7FC007FD5E4 /* RegistrationPhoneNumberInputView.swift */, F95A64F429959065007FDBDF /* RegistrationPhoneNumberViewController.swift */, @@ -9964,7 +9945,6 @@ isa = PBXGroup; children = ( 888017822741E5A500346E9A /* Boost */, - 4C9D34842369EA3E006A4307 /* NotificationPermission */, 34848D5B25D43ADD00E5034B /* about-mobilecoin.json */, 34848D5C25D43ADD00E5034B /* activate-payments.json */, 34848D5D25D43ADD00E5034B /* add-money.json */, @@ -10259,6 +10239,9 @@ B9D721742C87B8CB007EDA85 /* SwiftUIExtensions */ = { isa = PBXGroup; children = ( + 05104E392C8B540C00F8851F /* AccessibleLayoutMetric.swift */, + 05104D172C8A151100F8851F /* AsyncViewTask.swift */, + 0510F69D2C91EB2800FA3FDE /* ScrollBounceBehaviorIfAvailable.swift */, B9D721752C87B8EB007EDA85 /* SwiftUI+Animations.swift */, ); path = SwiftUIExtensions; @@ -10470,6 +10453,7 @@ 5033D46C29DCA8DE007FEADA /* URLs */, D99840BB297A04A300F7ED6D /* Usernames */, 052D17872C7E34D00023D56F /* AppIcon.xcassets */, + 05104D142C88CDB300F8851F /* Colors.xcassets */, B66DBF4919D5BBC8006EA940 /* Images.xcassets */, F0C124B626D4788A0031C96F /* NSE-Images.xcassets */, 881FF30623B5B1520023B620 /* Signal-AppStore.entitlements */, @@ -13363,7 +13347,7 @@ attributes = { BuildIndependentTargetsInParallel = YES; DefaultBuildSystemTypeForWorkspace = Original; - LastSwiftUpdateCheck = 1540; + LastSwiftUpdateCheck = 1600; LastTestingUpgradeCheck = 0600; LastUpgradeCheck = 1540; ORGANIZATIONNAME = "Open Whisper Systems"; @@ -13602,6 +13586,7 @@ 45B74A832044AAB600CD42F8 /* circles.aifc in Resources */, 4503F1BE20470A5B00CEE724 /* classic-quiet.aifc in Resources */, 4503F1BF20470A5B00CEE724 /* classic.aifc in Resources */, + 05104D162C88EC3A00F8851F /* Colors.xcassets in Resources */, 45B74A872044AAB600CD42F8 /* complete-quiet.aifc in Resources */, 45B74A7E2044AAB600CD42F8 /* complete.aifc in Resources */, 880FB3EE28CA53D400FA1C10 /* determinate_spinner_44.json in Resources */, @@ -13631,10 +13616,6 @@ 45B74A7F2044AAB600CD42F8 /* note-quiet.aifc in Resources */, 45B74A862044AAB600CD42F8 /* note.aifc in Resources */, B9B89EED2C064E760093A2FA /* notification_simple-01.caf in Resources */, - 4C9D34972369F0FC006A4307 /* notificationPermission.json in Resources */, - 4C9D349C2369F11F006A4307 /* notificationPermission0.png in Resources */, - 4C9D349B2369F11F006A4307 /* notificationPermission1.png in Resources */, - 4C9D349D2369F11F006A4307 /* notificationPermission2.png in Resources */, 3406D32E25DD80D600885B14 /* payments_spinner.json in Resources */, 3406D33225DD832800885B14 /* payments_spinner_dark.json in Resources */, 3406D32B25DD80D600885B14 /* payments_spinner_fail.json in Resources */, @@ -14341,11 +14322,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 05104E3A2C8B541000F8851F /* AccessibleLayoutMetric.swift in Sources */, 3402AA35271D9DCD0084CBAE /* ActionSheetController.swift in Sources */, 887F898228FF32A600D3B78E /* AllSignalConnectionsViewController.swift in Sources */, 342FFE62271DB2E7000AC89F /* AppContext+SignalUI.swift in Sources */, 3402AA4E271D9DCD0084CBAE /* ApprovalFooterView.swift in Sources */, 3402AA3F271D9DCD0084CBAE /* ApprovalRailCellView.swift in Sources */, + 05104D182C8A151100F8851F /* AsyncViewTask.swift in Sources */, 3402AA4B271D9DCD0084CBAE /* AttachmentApprovalToolbar.swift in Sources */, 763D7DDD27E25DC8002EA7E6 /* AttachmentApprovalTopBar.swift in Sources */, 3402AA4A271D9DCD0084CBAE /* AttachmentApprovalViewController.swift in Sources */, @@ -14518,6 +14501,7 @@ 88B987022880890800F8C74D /* SafetyNumberConfirmationSheet.swift in Sources */, 88B9870928808A8A00F8C74D /* ScanQRCodeViewController.swift in Sources */, 7677E41129F7A60500AC6A75 /* ScreenLockViewController.swift in Sources */, + 0510F69E2C91EB3000FA3FDE /* ScrollBounceBehaviorIfAvailable.swift in Sources */, 50597BBF2B97D629004681E1 /* SearchableNameFinder.swift in Sources */, 66FC638E29EDABAC00F00DAC /* SearchDisplayConfigurations.swift in Sources */, 66FBC4E328DA82AA00BD9E8B /* SelectMyStoryRecipientsViewController.swift in Sources */, @@ -15183,7 +15167,7 @@ F92E4C74299E9A0100C6E6C7 /* RegistrationLoadingViewController.swift in Sources */, 66533E3A29B9502100E8D928 /* RegistrationMode.swift in Sources */, F95A64F3299589CA007FDBDF /* RegistrationNavigationController.swift in Sources */, - F9D5C39F2993F9FF004891FC /* RegistrationPermissionsViewController.swift in Sources */, + F9D5C39F2993F9FF004891FC /* RegistrationPermissionsView.swift in Sources */, F905DFEB29A534F200BAD034 /* RegistrationPhoneNumberDiscoverabilityViewController.swift in Sources */, F9198485299AA7FC007FD5E4 /* RegistrationPhoneNumberInputView.swift in Sources */, F95A64F529959065007FDBDF /* RegistrationPhoneNumberViewController.swift in Sources */, @@ -18057,6 +18041,7 @@ buildSettings = { ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "AppIcon-color AppIcon-bubbles AppIcon-white AppIcon-dark AppIcon-dark-variant AppIcon-chat AppIcon-yellow AppIcon-news AppIcon-notes AppIcon-weather AppIcon-wave"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ADDRESS_SANITIZER_CONTAINER_OVERFLOW = NO; CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; @@ -18310,6 +18295,7 @@ buildSettings = { ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "AppIcon-color AppIcon-bubbles AppIcon-white AppIcon-dark AppIcon-dark-variant AppIcon-chat AppIcon-yellow AppIcon-news AppIcon-notes AppIcon-weather AppIcon-wave"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ADDRESS_SANITIZER_CONTAINER_OVERFLOW = NO; CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; @@ -18655,6 +18641,7 @@ buildSettings = { ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "AppIcon-color AppIcon-bubbles AppIcon-white AppIcon-dark AppIcon-dark-variant AppIcon-chat AppIcon-yellow AppIcon-news AppIcon-notes AppIcon-weather AppIcon-wave"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ADDRESS_SANITIZER_CONTAINER_OVERFLOW = YES; CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; @@ -18701,6 +18688,7 @@ buildSettings = { ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "AppIcon-color AppIcon-bubbles AppIcon-white AppIcon-dark AppIcon-dark-variant AppIcon-chat AppIcon-yellow AppIcon-news AppIcon-notes AppIcon-weather AppIcon-wave"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ADDRESS_SANITIZER_CONTAINER_OVERFLOW = NO; CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; diff --git a/Signal/Colors.xcassets/Contents.json b/Signal/Colors.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/Signal/Colors.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Colors.xcassets/Signal/Background/Contents.json b/Signal/Colors.xcassets/Signal/Background/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/Signal/Colors.xcassets/Signal/Background/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Colors.xcassets/Signal/Background/background.colorset/Contents.json b/Signal/Colors.xcassets/Signal/Background/background.colorset/Contents.json new file mode 100644 index 0000000000..9c0e331e97 --- /dev/null +++ b/Signal/Colors.xcassets/Signal/Background/background.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x00", + "green" : "0x00", + "red" : "0x00" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Colors.xcassets/Signal/Background/secondaryBackground.colorset/Contents.json b/Signal/Colors.xcassets/Signal/Background/secondaryBackground.colorset/Contents.json new file mode 100644 index 0000000000..5ae042d914 --- /dev/null +++ b/Signal/Colors.xcassets/Signal/Background/secondaryBackground.colorset/Contents.json @@ -0,0 +1,78 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF0", + "green" : "0xEF", + "red" : "0xEF" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x1E", + "green" : "0x1C", + "red" : "0x1C" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "contrast", + "value" : "high" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xE7", + "green" : "0xE4", + "red" : "0xE4" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + }, + { + "appearance" : "contrast", + "value" : "high" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x38", + "green" : "0x34", + "red" : "0x34" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Colors.xcassets/Signal/Contents.json b/Signal/Colors.xcassets/Signal/Contents.json new file mode 100644 index 0000000000..6e965652df --- /dev/null +++ b/Signal/Colors.xcassets/Signal/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/Signal/Colors.xcassets/Signal/Label/Contents.json b/Signal/Colors.xcassets/Signal/Label/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/Signal/Colors.xcassets/Signal/Label/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Colors.xcassets/Signal/Label/label.colorset/Contents.json b/Signal/Colors.xcassets/Signal/Label/label.colorset/Contents.json new file mode 100644 index 0000000000..0c600f92f1 --- /dev/null +++ b/Signal/Colors.xcassets/Signal/Label/label.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x00", + "green" : "0x00", + "red" : "0x00" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Colors.xcassets/Signal/Label/quaternaryLabel.colorset/Contents.json b/Signal/Colors.xcassets/Signal/Label/quaternaryLabel.colorset/Contents.json new file mode 100644 index 0000000000..d2d91bdfed --- /dev/null +++ b/Signal/Colors.xcassets/Signal/Label/quaternaryLabel.colorset/Contents.json @@ -0,0 +1,78 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.180", + "blue" : "0x43", + "green" : "0x3C", + "red" : "0x3C" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.160", + "blue" : "0xF5", + "green" : "0xEB", + "red" : "0xEB" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "contrast", + "value" : "high" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.400", + "blue" : "0x43", + "green" : "0x3C", + "red" : "0x3C" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + }, + { + "appearance" : "contrast", + "value" : "high" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.260", + "blue" : "0xF5", + "green" : "0xEB", + "red" : "0xEB" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Colors.xcassets/Signal/Label/secondaryLabel.colorset/Contents.json b/Signal/Colors.xcassets/Signal/Label/secondaryLabel.colorset/Contents.json new file mode 100644 index 0000000000..37ad6e2fa1 --- /dev/null +++ b/Signal/Colors.xcassets/Signal/Label/secondaryLabel.colorset/Contents.json @@ -0,0 +1,78 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.720", + "blue" : "0x43", + "green" : "0x3C", + "red" : "0x3C" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.700", + "blue" : "0xF5", + "green" : "0xEB", + "red" : "0xEB" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "contrast", + "value" : "high" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.950", + "blue" : "0x43", + "green" : "0x3C", + "red" : "0x3C" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + }, + { + "appearance" : "contrast", + "value" : "high" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.800", + "blue" : "0xF5", + "green" : "0xEB", + "red" : "0xEB" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Colors.xcassets/Signal/Label/tertiaryLabel.colorset/Contents.json b/Signal/Colors.xcassets/Signal/Label/tertiaryLabel.colorset/Contents.json new file mode 100644 index 0000000000..dcbdd42d47 --- /dev/null +++ b/Signal/Colors.xcassets/Signal/Label/tertiaryLabel.colorset/Contents.json @@ -0,0 +1,78 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.300", + "blue" : "0x43", + "green" : "0x3C", + "red" : "0x3C" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.300", + "blue" : "0xF5", + "green" : "0xEB", + "red" : "0xEB" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "contrast", + "value" : "high" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.500", + "blue" : "0x43", + "green" : "0x3C", + "red" : "0x3C" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + }, + { + "appearance" : "contrast", + "value" : "high" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.400", + "blue" : "0xF5", + "green" : "0xEB", + "red" : "0xEB" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/bell-ring.imageset/Contents.json b/Signal/Images.xcassets/bell-ring.imageset/Contents.json new file mode 100644 index 0000000000..bbce305b26 --- /dev/null +++ b/Signal/Images.xcassets/bell-ring.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "bell-ring.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/bell-ring.imageset/bell-ring.pdf b/Signal/Images.xcassets/bell-ring.imageset/bell-ring.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4203c4770564203cce3c055c061bbb85b252a3ec GIT binary patch literal 5396 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 G*1rJy2bK~5 literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/person-circle-large.imageset/Contents.json b/Signal/Images.xcassets/person-circle-large.imageset/Contents.json new file mode 100644 index 0000000000..88fe2473c4 --- /dev/null +++ b/Signal/Images.xcassets/person-circle-large.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "person-circle.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/person-circle-large.imageset/person-circle.pdf b/Signal/Images.xcassets/person-circle-large.imageset/person-circle.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8a22f7b1fa7ebe9e9ffb4d326e83fdc0f695a0a1 GIT binary patch literal 2329 zcmai0dpuNm8!w92gmo+Bk|U9E8FOWZDVK4{C6_Tvmb9mt;}SDtuGtiyvaU(IHLkOy z*sRuek=u6Dx_!!|HbOQ^3b86Lku7@95nAp0`MkeB&Uw!Fx%{5n=Xs8?ud}Nq4r7Bt z;Q%ba-M1eF08}bqC2pKucp%K&R?I$C8iYlSA<D zF}dO*Isb&YJr9d-7wF7A>2y54X~Wa(Gzt5dQor$LTm<@A#nWqTtw%dB#dbv!mh@fD z?gyy-$#w1)=j^5qDdwg_mDPzo{lyOhbDQ->{bZn~$;NbFzawkb4kUc&$h6P2&6Y=eJ z8CGGBu6{VlJN&M___#s$j8S1BtE)A68tc)u+Xo89h*;;I+R5rN!)ZKO#_-portKjq z`R4RJx_4e4I+VifzEtyEMHF?tQggExhZ#3(?|GIP{)LK>4OkjDCT<$gE z2r`M`23{1)-LSU2*IQr=p~Kbno%T)N$ft}d)!Db!)D?I36xdh}29Pz+$fTZn+uKbl zxGb}unU!Ut*#XCWZZ16mZ4QQ1mAgl)L+Kvzv3gD{`QtrNn}_i_1NySC62Hu}n<@fp znH+O)WF3$C@1&39!~iB^oHl4KtK`C0NYKn#wm=;#<5V z$6=_)r;X*`Otr0Py&Vh~y*xd9MwWA6yPm-I$~azM+F84^<|xN;qiM&c%!7BShOLjs zMDI4c?bRJ+Tu-Bx9nS_o`Zay^+m#Joc^mUh}%Aq98_wE@#cGj1@ zIpELo44>+D3~6ipY2%fsZy6@z_r89BF*wuSwsvf$j76@>3K#!IIGNqzs~~z2$`X6} zzKVSHS9-Kr43v_dQFQO99}yDokIc?X-rV|gvt>m3gYyoSwY;h_>Wq9zi=tb}28Dqc_oC}L z*UnbywA|mFptrT(!?+YIwLF|<-D3Aq%WO@VLo-9;M(8L&&=yc)nzf%1r)S4UcHFeI zv3(u@sEg5R&c!%-jwceRwc(wZTLTD#` z(2Opnch4N92qTU;dD;WR7Nw^;PX9;Y;CvHsto`<5tPvqeao|yd`(yc_sqfl`%9(FO zQBS*cb7aZ+K5`TD@zzt-XI)RtR7zU<29ziUiDFX5z31~q8Xx6~S7pckkHLmaj!(-9 zhb5Ah=a0=Yys*PK9Ij9R1n*hgMvU)b;C1E;hA-`_T@Wi0bmGPW!2lM6T{xc!B6$h= zOf6ZWLjtad&x8a3G9{4_hAc)Jmm`EY@QuhATmpSTK8yk6Md)wC5`jK1T+c`r>?(jn zfK1^fiy~MT_YeOCF!DD`loghc00qkzB946uoCXTPXm0pQ)mT0hhQb0kEb3GI$P=(8 zkq9IJ#upC`3oK9oX9*(Oz}_S0Gem+vzJv(>l%jkog|orI{;#lwvmwHpYb8X%!CCzR zB7asC^1;Yxh>t?*?(PgH(p11@LukM>l7;@1PAfW>D+G{KEtJN}Kk^`i#J%u$0`i3m QnZRR-WR$V7i;wGn0s4ZF;{X5v literal 0 HcmV?d00001 diff --git a/Signal/Lottie/NotificationPermission/images/notificationPermission0.png b/Signal/Lottie/NotificationPermission/images/notificationPermission0.png deleted file mode 100644 index 08a30c6cdc9a921ff2b956ef3619382b793cbe7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31425 zcmbTd1z40_^Dw?FEdtUVf}lvl(zSFqB3(-?-AK1eBO$SLcS%W?boWXKh|(!3`K|Fh z@B96)-@iVto8{hf&zYGsGiU1DgsZ8@Vm}~%00026<>jO_003kV;(iMQ4e=>pnbVH= zgXtuv>k0s15&Zf^0>IKq0RU85Yb_l&9c3j!h@(B5skx(>1)Hb669O6l5Ek=vGKJV$ zxY3$fSXn!W&>gmP(9v3(i_pE~QRYx~lC*$Y%Xzz4XnLz?LA-4t0_JpLqO`)Ef(Qim z7H+1rp7wSQu7aK-bieTmBJO_;v(wT3263|$p%ec_kXA=ojaJgp#e$ZHjh7X|3F4vU z6JX=y^U~m|F^JNXh)Ah4>~y2X%9E z5@ct8_39PdD{eMN7b|v70RaJa4lZ^sE>;8ttE-oTo2e(OgDd?%7^Ey*AuiTVZq|+t zw7)Q#nmM|=iO?Z9{j&snr+?8pxc(IrLSXEkrcUgfY#hIq^c&C|@-Li|yNlg#;^q)` z3p)#Y3kNq>1T5#juuf1%H%C{f?fy}e+CMDQN=p7xi~#L3 zWmAZ?!>@HP{nsoDDN{EK5jrjoE)G@>5GyB#7N>w97l$A>4=V?cAP2`EP-RDRYfG>H z36z^(kelm&14Y=4xv87!|0}RLM9|XF#oiRLYHNE_D+_if2P-<-e`P5s>1gNZf*_3G zj{CpISpQyv5=P%p+iWB z&Dz>rkPl=oz$3t6!OCqWz|RUYGv#LG=j7pIHRm_y<*^j76fn0i{b#$%?A*A*H&G^_6D`5Y%b^k1e{r|eo z-_QPQoBsoj@EyNy|8-i3FaNqd3kQU+b3r(@h|(7m0D!x!yp*_>=fZvlx~JBQFMYCF zb}Y~7BB%(7Fme=Y$}RTG%KXb($_^?kGvX)kn}Z){M+DMIzl#BF8w%!Jd=bJ7eKLJL za=q+&nCNpj@+2X%^ICNLdc=HVEW>9^sr%o!&STe8!_plTHVPDx;!pXG;psXH1`|>ZF zrwpquxV+z~pcrg_ks6+Z--0!jXf>ydLnHnfd&tHfD4ER!eD^r)??kuBAoWyW+7A>^ zFB0bs{$GM_UE)PEIfbnV8bM0_VgqG=<#I*mrjD#qBCf294vPn*#$QbtDE^)D+DSJs zgA1M4f(-fqi~DsD>0S3<5~2CQUCQAS%DSkquYlCu284|7fQ~xBPXq%sPJ!Zw#`NS&*{BB+TqB>;n!6HF`)2 zm2A6b=g^}iR$+b{U0R*+_x1QELwLQ5w)R}0@G2v9^&qpJ>m)*yJxVA8BQPFj@pnb1 zKRqvb3Z6yeBCg_(t9|ry6Pkre@*_{-iTpE(j1z>+BPil%QGIHE5Ne5c^<|;M69s1y zi6;nAN2C-v6Q1%iGnaAGbNzuJd|_^~F5q)+$7kpo1f7IInV`K7c)nvpe<*Rfwk!)> zzR&whEjM|3dx@;TR!?{m7Wn`gfodrgks20uY%BAZ``)Kn(?p}mx5MqrRCB`XKb_l?B2y^*%Rup z4?s{&F1J_rLwfFi_!PvCgFREz6rfSl+y6+hJ+y7zXS?S)^M2_TP zS@y;ogX}`NJdgz@^lS9kQfKo&Yk01Ye+ELzE`h}|BE628N|erS5^UUx`fNu%T!r;l zi|<7&tgK>VxzAXt(2y(;f}#`D)FsL|J}Q&=D<}(9vkJC~&t>0{c%;b-m~6ob`feL& zGFJbvVW&t`FvxsXE|&``Km~d23lZ=L_VAAWBZ!9Y$DT^B!=^q>Q)WvA6>@A_!nQ_Y zsDv`t=R^Lkbf{gsLhmdd$L2@G6e0h9ISZ0<mB{CXYTNPk~h}Kcb{aOFv?nX|pXGJ5*+KK@Pl1_5)*n`*H zA2W;pUE6or7LWZaIaJV#NM;0Gpt6)3BQvutpacJ3+=+2-yR8F~JZ(=-GtBuU`t3)EJr<2sVa z5vTs^dn9e)i}ysTex|*UEWP96Z?fLc{k8U0>tkOZ1lUZAo?CxA9;#q<+oKm0TbRz( zX)S!aLm71PcH9<=|EPASt#|zE70n@QQ!Z!;y$;v?kM_O;WNEhpyT|E?=3M1pD;R$aHS}n7+~w4O=Rv1`ad>3 z;uFdv5|A71dnzefXGPdrlrMh@+OwAGM;`Dr49stsey^W2e%Tnf8D)=UgQa+wnqt<6 zb<4UX(y!8)^IPQ2{DF*m3%cqvlZjs0m>csijy;9RERH4dBM%rwumGIufrSnq0x?9M zz|H5QfdKcsAXy3|sU$dn(6hDSp#(40qmtMfY1GHZ3(fYfAx{)hoTQB+S$Aw_`v)!x zoM-l-eUZp4aF3y$4var4`c42X?N^umwMmM}fdgJ17WTO0aSGZUijTz^F}MfUFrpt~ z$s;+bu^+v7q{gRkDnl@c85GL!H3h?ILOSr<=7Np{E&l#lT&%uXXEdvmzX+u+=rD}} zKA@tpp%92o&-<5-EW&9TKFluBNQ_3p0R4QYqbByVzTk+KAe3y-Jt5=~WLiqRJB~zg zYjeU?9PyBmj$A%28=u>oj)p)^6-y#?qwR%SVtzEfw2Wr=W?7oKx?u*;}WQvF+vq^@(cRoO z)p&;Ip0k6jI|Sa*%CAJU8DKmXi6X_v&oF<+#vjYA3X^?Qf2$t zz6Oj-1vw3*l%1O$P)jq?)&ptp;An#UCPHk|l;A(U_=Q6tqgXW35YdEz;areR9Rb@8 zAvdZe5b%x=lL#;NX|IPw{EOPvsxrm>seFIb&c~1C(+-G4zu;rasE<`B)6_~)mjR2z zj;+TWr(^hlLvHg_b~f+wM+%?=;6MBPy;pXLQF_MM(DR8UHR8ZRxv7z0^;C}_Y3~n! zh*jV+KITJv`Zchz7(s+XDBu%5net?tsQ7(KGq3FX;G_xdSJ=&Zq~8tnCc?<6G2cYJQNwG3@4lr18nNQbEP;Pq+4EYktB><5LHdbL@8qnWyFIN{^G+oP)bY^SRLf0B zaj`5sQ%cnL`^JC)(p#;8#^MIOW&o1oYrVeRnYVpR?`sfzrI#y{z*1`aq1a>~HynR9 znwVj#O9Q%R6Noi4Q+Gew*FpNn9#LQ#=&)j{F$FB>9cP^rt>()Nqoi`9MA;xr9`F-u zq$iJU1XGAH@PXOyvSR|DYDlclFoE*xlC+XCwkuVfK(Kz91E)hc zew@0v%%U7=I3P9ODOAe!pkV<+IrzCou7D`h57_BA14^zO=HSl!cBP0siM&l&yJ+M= zEAgoN9Cnb7`SUdYblAz3jqlErwC{{af4y$d8!){*OAOX~BjRAPvN2-Ag%s(FbOs;` zAz*@Gzm2EO-PVHfgP(Siu^&YG#DRbZDL9*HvxFV zKuIPa6AD97_OaUEonf-Vyuy&j2xL*O94=L4)$9sG3uM7Vq#*N!LCFnCq=y#a57ZGL zFEjx7sFzRl={l7`m?nvTqIszSKzUXX8(RAQ$%ODvcTw>|LsIh{hO%8@I+AltyucjP zD2M=|fF3>Fp ziDzyGBJ{F!)LWFFsJ-sbiA<`immyPqk`ne)Ei11Q$NO|$%ZT9n(v;T@Lw_Lci~ZTyO9(A~m#dheA*&N#A9?HDjM{Od z)_Jy__%8ZD%E)c4B6_eU-Aa=*~=vJ#1Z`eENVmf?rD3U@}{<@4qw z3%6f87m@co%hOO$?7Js%k)*oaXX*v)>zV8^pNB<;60k09i%X}OS%wR$cFoUV0X5>= zMdd7xb>*_cp&Ng9t|1p#GYFigaVT(pX~LMImqgigIVVF-NAg3QH2kdf+?SY$BNPd| zN2Yl~UrQO}Ro;Rfuxl%O>oIh~Bi67ghP==5`R2&;gt~SG7jEc(nzEHRW{rvT$+gYi z^NO2-+72R(z3t2p3<)O}R|tz!dC4w7UM}?ocT9Xl5N58JcrlF}{i7*Nd+{I3+=xdZ zuaSpTau5Jup&o0mJ=@F`qh7Z>kA4o|Ka+@bEo1!fC^X4iUY#8C33F8ivt$(Ng?#4^ zwFshpSpg3F3iP+reghvak6mae4je7SD^yv2+}dr|+!W7pwD=yN>+4T^h$Qk+{prNJ z7%ROf?2@>7Os()~*<{M+EP0y#mSh!!O4flOY0sVZ#}maDX5_Zfax;h#kD!kpvj&7!(9k1*A(lJf&?+8UFObq3L6X zN_OSO#hHVh@K$d*wnLO*<;?g)*zPA)EQ1fSEY1)c7<$>cXSGMx%gPo!by}q7#!Ai` zf~wZnBA0gY1j>TCMAg;=R7yJ~o6EZ3a(SAYGrZ;3PM+3j!gyNw z7W8sE%E`#c;7zVp17A14zMHjSjl1}h*3eBDgn|jmGcb{rJdX`ku^9)w2Qb-&D)34) zyEf_R35UGyo(7FfQBhz17@!t%{_=J$a#_xfgss65-W7qipLoiF#9#l-4OE*R&&V=0 z{mJz!5B4H6ll@niRAS+U`BR+CQK4>E9YMykmqot&xqXG1m25Hu1>x#KPmB^$>?YlO zu#jVU%={(rvds4(U5VUr#5x*aZvPw4){`+I&$ZVibW6?dwk*0Io^05@#in>OmLW7e zrubF(tMBBO&9ix9*$)3(m)B|ZvN54w6k8ozhsJr(y4XB#HnRe#=WS=^lNNpd#LDc) z6W<3l z$T^#Rc!BJLIk<4^*6nH*USU2@tHgbUhg+C(qJ3q0chYd+Z(F zAJYG=0UmqdhXVfu1mcaV)1)OO5zq6lH_6mnj-{7)(Kz3F$@|#@AN|gr=q4UP9~8Ip z&O9ti)72_5?UHVIQU7k92PijJXvDy%&vW*qYty!|VaPtNcRd%o7FtHByo=*G=@li3 zhJ$B~w>*XdUC1nFW#%4OSVkChTk)m6bu2)Kh&M$CjkdTphe^^TfRX2RHP zzUoP#*YoyJrPK~0%O=_z6;`>HO^uZYL6uEJwIEeR;nl?(kL7vZ?w~yoJen-r(gkto z#DnwRJVe5$i+@Zj&N9|p5tdR+az2?$yeF)J`o~I+%wNb&Bui1m zjFn%R4)~JDu(?CN;akwa90OkE4(q(7eC<`3OoaQYW@y`r2YEuXsG=dzI4~NJ)e!(m zPD9qoP#9noS&S_bs~v)1$h`T~bZ{@_WteMo6W$C}YI{;fya&RgP)yuBb+yJ8&wnVq zU*3xY0zL`Ga;gc8eZNzHj$h+6j>}S>9AN6nUKmt~-KFkaStN4@E1l~@#Mm7g#IWQbR77L{y% z%_(dz(fFyXZM=I4w0TR*tR*mECa^fV69cZGCEFJtoFz^BoEZ8*Z3&TM(=|y;&ft&t z>y>Y!VeQOgLL;8S=yJg|bo7&fGCKKTL_`zCISNrcUru>}*uUyHJm?PqECK~a2ZWhS zdR2!RW>7P*mo7@nSmN`PST0N!a_Q3$>09qntemG&aS1IcC zcqmqY42#EbiLH3Cyix=(pgD`c{bDwjp){8>d{BG#)(H~$6^;Ci&X~r1Qc+6b6P=+B zu2@r7u%J70)dhwEI95;sAB%iul|E*;VvIoyOb|5`c2ZWwP_+U0SDg>RuJ%m;@){>c zE9@Tm54;kOVwO;dzsN82HhJ0~Ss|gB<%s#Jn`cD9Cw}CpDAf2r^2JAY4Idv8JIHNEU;#>vzuh8P?-!IZPMa!RID8p)1THg>1ny*Ay>DM zAgNYxUd$EBnl!dR(2 zr~C|_oWql(aYGDr0=d(0MBJ~2rWK9&3cR8`$vNYAyYwu(A#4m~<7>O>$mQ14mWQI= z6<^B0(P2unvGE>BJ~MsfaS_BweESN8Z)8~DXUna@K3!qbzQSj0O{MRVpO_`-x&fT<=4aqPm8vqM|+rNP)tDcNHBwPe`b-$bSuTovL8Ox zT+efIlVG`#E~>36YB6cJ9$q5Rl1gB#@>pN1RCsRS)S4^ad6Kp0bY%C9Clb^Y+Mp2n zT+d$V)?+T^hz_9Y(<+cVSNJ4Q=2_LP=_gmZ=J-X+%q`ANC zV(d}j*rA%?MXAWr2d)ENRK6 ze5zvY1HBPWI^!!DBnEVl4efyKx?)Z7_;?@e7(^8|Td!n)X6qV)=H8ql+RI9vjTs}R z7ioER-KUU6{~yUc2Set-O1Iyhf%AZA*T^M0Dvi80ajab_xzv4Ru8Ksy)#ehXA}G=GQSwnqfW<@JI81c0-4Q!_nrFqgNzh3_#z~lL;)Wf zjNeJOD$^M$O2tZA6V7*)XmXz1!d?y) zS$0e!z-{1Vo=o-_5)~Lf`ydaQ?@pBz)ZT-*1~5~RHbheINBDlBbnY+`+V*2eMw{4T z(3qNui@n*C%&W`^uOE+Yrm9=0$rbWKYzHqSKJm*TEa=P;Lm$OHYzD6!$|L@dyLkjv zK!PUr*6|F{3FAX|P;DfoUUA^X;r+O}+2WcD@)3Plq!k_}-9PDp`EclhSbdP`}4r0 zCYSoS)!09fkv#nqGH5ba-K${bAJ2iXFw|_!!gK~1Dcq49LEP`{51baP>G^ZtY}8lT z!M$f9)fJarC^lA4>P30;X2)^(m+oBddx=JYXru0f6@`*%P6`)dY2Q8taf|w1<{7GV zl_lq+y>}W_D!!9LXFZb~*R3Bf*=R9(1ociE9`P+EIsIkn(ad^=3Jw}sni*e-aY140 zvy+7Cds!3{#a)Vl(fqtgAx!o=Te$zFQXuT85Y{!rRqMe?6*f{#kJTj?yrsoReBL$6 z+1uSNUHamS6=P1t4?Vn8JRa`N=)1@rtR?qy$pgmvC z=-;3*y2;H&0F@Av8JSGYvck_27!Kc>8qqYyB1oV!k%yjnPj{{oJp*O#4O*|4ipAxO ztMJ)D#e8Rk^A$0jTLlLuj7G_6Gm|uab*4-C%IGo>8 ztc*AfQK(0}_PJGCfVp%uoWc`CD)>{kJGR<8>D&*N#T7s@LHLT#p3{R>XNU`8oK5Cr zaA5_ZSp*IfY{LE63(~y06JuWddYbGylA223yf3Z{nXg;DYuIQRjIt-^>%N_dPUbSB zzD9Fj&@11HZ;k_(o89YF7i6LT6Sgrx3@9L%2dRt|Bjr+r@fB0Ki{-je^EMWJ*J2B6 zSTo&`AZ~Tpc+&C~yfbS1(I2ARibLYI8^JqU0yNZz4)6Ieu!F+^ZTHs;Z&3BS*AIk7 z!+pfKY_l0qB4KW|M>l-XDr6YG0wR1de##Zqe##XWUl234MNi;0TU4C(AnfE5B5A#N z*`ri$tKVbD&^rzhVx zN8i?GfAGjcj~3LE*nV;0XVy1Aok4O-;??KPw^PSCd_qU3SzmBXplu^WLpgY~7RNT_ z+sLz<%if%Y{B9adGH9(949;_Or3}SD5;PO1u5hmveU67av6aUrUXAHN5P2?IlPmD^ zH5wA-6=z@bd26n$cnROCR*$YQYq6`Xyz=h~1raF-*e9OYlEATNO4LiQa3&)@F0*@# zHZ2=vBZ8CxHys^T?&D5HKRZO}pZqDgY5m#T){Yp`maRZWn>SL6Hudf4JMS(kF~`fE znSeTUuq^EUm!tp_Pzqp*qa355OIws@#F|{;QHlqK$+1(^j#LP2zy+$7IK+`-2G^Rj=clDQc#=Jnx#P5nnd#`kPa3yg z1Kz9}u^x8@@@G-!N}tIHP<_nG4%UtdtjIpPZ`$s?7E7(tudfBY~ z6TM0o=c259bR~SIQ5f`u=5GedBmJebcF~>}C${8-kC*s;wp~>Ew@yqHMBulzjPGGm zUBd_xm}nc0s%akRiCCtd!Kz~-#&T4bti21oh`*MIB&^-^*8lEJ&KW>w#$yrfQ8~AF zl56Ut?wI$sztgvI9;keKj_67J*(U0QzjR#oII!74l86i=C|dUou{u06GcQ`uEsNGh zQt1aLox-$bis2XE9~v`TVoNR*R-7bX&-d?m;f8C2N0))~?b_KzL8S?)mhFqSn{7db zOo6Fj$$uIDh6Wmv(66s{ppoMv7zN1KXbz6aJ53Ao80gb4fT|V`o6I|AtZHw6vM~s$$Cwy7nFkt znF$chx7KeRaN7$nNyCv(ts*C>M8vX}UTMRz0q3=Y05}IBDtDO!Q6OQKWY77IU!c}C zqBQ<rbWoN;e|78Sm==4czDn}_d-?YAKCv*8|f zZ)!IQ1jC`V^0M5#)DPZPL}`n~%fP>HDL>L7w*gP#f24^?6VM!QQ@3-?fG`;yxIDx;b=S#v>QxzNx87yoc=e4=Kfj->jVnPeAj@IllU?R zfcvWeUUbP>jl5AlpAC&lG=8j~PzZ=%=7#kE{5%u?7XO54$I`~n?139U_gk>K$coK7 zOc828mXyqYBm*0G12~hE-ri}(Ml~l#)5w$}+~}HP6gX32842N``|5d|4u5H};my}E zi$o>7+XGogs-(q*aRq}DocIeBE ze4rz7Mq&2&&dCbv3zd1lSbOc-WM$VVJ*&$%8NgVcAuIe<#2L4^E|tGmCAA$@l#Bey z@?0qKlyLj$YobHknzdu5pSx7hv%ww&t_Ucc9llu&a9VCMbTI21y0u`-}(~a)! z-)X1V`I9ox2=4YoxjQt$4O`j7z3-ZgwD}EgG|J8f)mmC##R|X02jJ*$eQKXJGXHkD z`W{|2tb=aT$SYlVS^u(eWDckd{8zb>h{!iyJe@WJ&SNQ+hNZ&QG%FCr7j#NoKChG> zX*2+B;WBz|dv`lVEmS6O_|$zNvGuMt-Lv|=JThnWY1ZopEYhIp|Jt3!J67bMbd=ss zXRx?9w`?sG%pux-O&*Ap)TWX1@%NWP9g~9LDo%9Xvm6uWS8oLt3Ng(zH3WjERm3*+ zisNjdg3pe;I04ZshyCFMPZf@DgL@9!Le!Qy-S-O>td}=@`;?>0TS=R|T+2$nI8#;= zY!7*`r9dnIko%Cp#J6wXR(o*8KCyS)09)G3#XT31=&28_d$`+FzxpOI9L!rdz>$Qf zh%a5aj?75ln^n`ohv$T?{v&CMRVP#-RzS!_PqT?;=B9w>6h8T4$?4wZ{UaK|O0qPI z5sA?gw#LRz$cc`btivnZtf#S~{5G8bSpm{cMyYL>yWeIuA1^1(UcBbRLmp0e6)?ZU5N2;Q~IvhVHN((wh53C~TJ`Beu zCQF?61;w)vj)ErG$=cKBT^x-^jV)i z#<^vAU}WUstsPhDM7y8Q>F&I$iX^kc?a2wUXKP0z7!SQ>^<2<^zl{akD)W-`PFAn$ z{@_Ax#T`ZYI%UF)_Gm{=Y|Nsc#(7BCA2`?HhH`ht$^uD}y{GqjjCDSi5Lh|A#{Wo2 z`Ea=kcWuoPOO!WGBe}-Z$J5R+pn_~Mvgd&)@ARsddaQGcLG`PQ^iBAmzJz3UI40;U zq{Pyuaena$Yx5{+4l=CkB;CbOu6i{CoPae^r@2vlLMrQ)y`7Cl9{Ck#AuJjQN8a1=d*R%;N=vLQY>!Bv~a6R2$2>tRTj-DoHW`c^3M?G{Kti1XksGNB1FC0L_gNNep(AQgDjG zkz=!QFsx-&wG3nThS_vMK+YAWS)`I98%qsu^{Pd4i%&?Xv7Rn8qv{?OxyaFenVo^c zBbl^ea*M}@lj+{IKP&sTPRvo|0QZnDa`mU3AY1$vY~&eGo`5ReSS z+HYUJx3q&;Qism(GEczA99Jl=nZKWOU76WrLVmvvg$M$S&nIT40nw<@i~UAwk})y5Fkyb|kb84BF9CR%N- zR&=dAJ$3WFIqrvlbcSrzJeGVjux&+WrfIMc03vwRo*XR{AE>{0tzMsj&e!lN?>PBD zZFz98jo@9vF$rh!#I6$Art?k$%{v}Dk7Cg+&jm{z{Z{mwdZE^aZoW@P@}2K$op{Q* zUv@Ql(X`57wQ38g1aJc7ZZIwtU-7$|-G|GK6E0)8=EjUt1j1bVlLsItyZv)J=;_vC zDAdVQWXdyGMBm{z?VWJT@<6{{ZO;!%Kj2(ws@i?$=8XpZzh6=KolJuvm<8{daAct~ z;FG|*tviYN9NP`=SI}gi8lCz1w`!f2L~KpNsVVyEjxzyC9ym`c@$J*zH{p&q$zvr2 z#aQLemDJIJM3XTEYAZENENa6#XWnIRxO^KaL+;RTiV0c&(ita?rn8|P8sNu+39Wn` z5RG&$jh24h#(5eS)Eh~XXTR7mFs7Y7u;w(N4qFMhZ`0|Iw|6l)njwJBd61Oey0SlS z{ea!*>-4}OmSj_PG_;@THlhWa!^)q5=J112;G%=X`D7amBdXWD`-@BfI<@MX{jF39 z6FsRv*+l!$ZO=!9{%*98_n{NxRNP5tpED;^w&V#V4oXKDltso^8&`89^D zv8>l4_us~Q_BnbN<{s)+khy-3>RIwe1+a z`lco|faRAc zqFp)@SY{;Qm(G6#cSI!rd5;9K%~oq zmP3hHR>Nk(<(5~;Bs0CNU5p(S{IM;ImS&^p4}WsT4hOT^I=p|@7ueGx+Vx>Y(A02q znB%5{h@)VfegH12Pq3Ww;u`ak`i`{W%m?efCH0cUY>J?bGgM{|^|8}XsWQX(ER;C2e=VKn>P zqRud;GfbeI8EG_;#~~%Z32SWHieTTxO;Ke2!9XZdt#dW9-rFf9ffL9|pirEEZt(j8 z{29BCifH8b%@6Qf~&3Cny`D%m3X;rwWpOjWU{HvPrNyAo(gXQhG2 z#uQG)c}TSh*!*d9U7dUvkj){Q0^2jQ$&>rnji0ZbSQe?h{Tc?to6>UcpZh3j^LtPx z0yw!~0xd6I+Bu#RD_Ji_nA_3ZxuF$t*RRB-mvjGsr>me(`G4dyvlD7><>uhU<@MMh zaJQpRHV8v%!?Ca{9BA{K^A*#gUVJUUd?_3BSEv!BPzff>!oc^h-4kN;s_}SeV_)&D z4>PGeiLPFsZ6_91)GX3Z#MQ8*5E6uz=Um}S|!@tOSCzr3A0Q;GCTCA z&mzD0MOL3+A%7^8A1>BM&9!L!uY13QTztnCUxf9szq>L=LjwvAug z`NdWUX))-|iE<11;xj5tFWD31xuXl(-R@387HFb=b+gK7cU1`-Q*C|NFUn)0h&woy z8=z8(G@XAPi8k9?_Y&4MeJRF&arO+Aaf-EgnU%u50~IHIQX;a)uD1kz1widdvZ?1A zyytvUpoklLd=s9KvV2NZ?tPGv;xqNFU?s`2kEdbeG_NVf-eaXS<@z8JR+BOQVHi?& zM#SOKE5Lf7kTv^RR!pn!eB$i@rLZ)2Lwc0!4|tLsFO{W_<*$4UQQ0ynWKT*VpEj-) z2LUMK^v+Fe33eLZ0}3-V2EKfRka~XFO*(Ko(WjbNbEC=Jv&Na32-O@wxtX;ut8;dk z7%NATcQ($rBS9H;3hgtn0n=+#H2BWzR3>na<5$z&KF?N zzX?H_^Lp9YQG-@}tbI98ZlZ6}`LhH!tO1&HVrgM9SdrdK%_CvfGx}=9k3=?xa?&u6 zr0;Cs#86AxL1k631-UKn@cFi-v<@i7sK#>#Q2zwP5%jG7S4j>ThjeX19Bn5P@B?auSRDs{w~-pb`>I z=m67f^gv8j;_s|;k<(Wtu8$9hZ`OsF+f%-RX$HgP*U01Uu|ISloSvkyZoh3B1K&nr zV%jUqQCs)(aZ_Gi04Or=W`YYdw3sYmKj*qtI0Uu5!Rv2VAViCU%vdkpk_lbUc5TO@ z>1;-jlWkwvYPy@8v>jD8p%tRO>e?K7^HYbM*S##THaFAqJ9g)l?Kf}oO$}r=r6#uA z_pS~@DL?6yTq}bDAR=Uzt-BPR8A*Wth!!WyB_gNql%w-uW0`zwT5m9mxTE;ftP}hj z%nsvNTCcu4L}mCq*iB;ZjjyWeo%4#W@!ybybJTNkS-$L=t13Xf-&8U1h5^Fm}K|#H>f?N|)^%KOCDKD?|YVPYux`xIANyuB z;;>oFXk047gNz;Y+$2Z;4T}mG(A=4c0r0f=ZeLJ8%0%mB6KZPVQE(qa#iaBFcMXRl ze%MqPHXpnDzUbO}q0aCD1wYJlqb>V5p&r!l=aT znCN?>D!D%7f3o8hL!vATs!<8ZS@Z`5Scvu&eu;>l-WkA)noXd9eE;aJbFzyJ7i&VB zDl&>rm`Yi8#pXYkVdbasq)N?VT)WJFK9H)3M2$J;!s{MJ*&nxl)GmfW8i3KzW2JZ_ z^z-M(Y3O-lcPy*x=Q7y&*pG|P2Z0yJ7Iqu^vX|_?Tk*e+QV1tfB=KMzohcIPu!&p5 zE`O#?vTcFY=ndMu@e2AdA0hXG0PFPrVV%(rW4pMBB5)?x(_Ft=J@hfp?>~t<3DQ^k zsAl8HG%=tslx$5j7!E^YP2tKqxUVX?tA5Ysl5h6mo``GN5`flcjo!sjaU(_-elklm zkgAQ;&cmlLzVZZVwBo495NEh`*yPKSq~J*Oa!A_RUKRdjc~Z93HKa4C`KY3ea^v$9 z&})r(!m+tz?lrd9+S11dF3qWqw;@h!_XI3hgzpv>HW|a;MljNS^jbl^`5r=l*|CXc zvW**EW8r{!J#K-J`6;Ngw8lM-cf;R~)LATMycB7@HR4Exd-bT+eYNyfH9vq!eDRXP z+5Z6hC<0r$QSPMS0F}RD4P)kY?dH1^7n0St_0@K)pQZ2PxQV^&szj%&N{2vV1i(BO=yr|%X_>m)wu(Y zfG5ju-?pIa2rr}_y>q+Skjzv(SIn?DUoT*1agizuZ+#mp@9-#l?&Ehv`KG%LkSg7^ zfEB}#2k>Cv2PX2!(P8P7lxmoTWVx63dYIY=YR&H}I>c4ThK)|6e4~C|dhcCtaFO;p zmq`h2Mkokv*4dgEyOAlSX{@$7JxXgfj)}sY?`*3Ss8;^bS9(;g1w+v}$BFhL+tl_C z$4ZcP+w}0%HpW^s3&GDIgLZ!A5hEE?Tc&2uw5cU%yRPcLht>6!)3liM>{;rFl;CNH zWAvq<*ElG#T2CTo_0NJgzUsf@+2^8!-aRtFbbOq*5)dbF!3a-!5fqSCx3%Jxfnxts ze!T^=uU1Inuy!r{)#?E+()MmcGW`dOLIZYKY=Yh~aC>dZ4Ht0a770im{2 zEXtj#8Ldlg#8ctF7ix2-&4jUaeN*islcjty?%)SGLu8 zV;|-hihLOjo+e>yQ|6Ucj@tqxrw~>$U75x-d@&g|Wrr0#b1Ft9Pk27cPT7RnFigCv zJB4Up`qV&76q}wI*I5&z-s-$c#ShXAC@^4#7@Q@gXEs!* z7g$0&VUS|h8RK~HES}T5)oA2yonwR;-GBdtzWu)6{PY@%%ouRbtgQ+St%uNbUe#Qf z4PUm@K)R{mJjc9y8NSRos%@wOiVhNEr;AReE{fJ&#$@eOeBF(^+Lz~Fuhof zowj&a<*+@=pF)g!P-{ts&jmTY;ZkT=HXomRiWyO%wsJi5Jb1b)J;<(@*~HpWT^Pl> z2<`1-u#K>?Luc;sHnSYD?ve)m$R_%WzPF*6z?Y_y6vRe4gU9#iHTE@Lg_CL?9>i8Aa|X!7foThe(z zW=G}$cwbw6qa}~n>66tI2<~2)2O!rREKEnu>D%lL;;ee?r{#G_R^9@HyEQI71LtR@ z^7^4=1;X0v831uH`Yy~ji&+|hmYdT@g)gN0z6qI%f@t)`svb_4+pex?sXFimM$bOs zZ$>+?z%N$nKuQu>Uvq`64GF~LL8#+azq@$OEA~6Q%a#L-`XY1nkvX|_uSB|~J0xSR zw9Vp+`Bnp0I$AJSZfse#m99yxA%{JE&OFuj-G*?1V~@Q|H=*-eZK}70OTp{YjzQb8 zX>lU=dLJ9LgI;agAMH2E>Yc8j-?!^c<#xPr^GOEb&LdwtzTV$^?u9%;zs+{j3J;6z z6FmB=CYDJ_%X)+kta=E%V`4p6i6g$Ii2~@!t4(X`-8_v&5{yDkvSWu5_Qa|)ITEkH z&kc~)bHabXRWe^ePF*yicB(|ze|y@l*2vM{WP+q!r)gVJh(9X}y?I<{-G2l$V3|oP zf>qz9G?4m?$s}uFuv1NgXQte^Ak$rnMXzh+dalcgGvs-{KDK7-r3N8e+U9W=)%yOR z24Tk|SK|XxS8ofc>QAVB%jG$~mxnZc3StEFz%QnITpBrBPzdkb=Z9Hg(#{>lB{d^) z;Pna6-c;q_fVed<+rJcHZvkuoqMf?;B}t4_-JqbYNB5 z<95QdRrPMfXj$(L-*2o^Z(mjQgxATC-Yn9#PVk|}`eF)yM!(#h@9fA&y}y#c(y)r6y20>?`=%~v-?Pmqa=kGvko;&_QsP}H1Owr5;pH&B z$PS9Xt1nwY{_%x-jNxThM@}KW>1Q`|U+b4W!bL#4w?$pM9H#vw^YoBzA}6*Eg1gah z{Ewi-Y?rCuex@O+w-M7 zN1fcZQ&N+Kd0dR+> zdwR4sdC&=24lH_dU;mcI@f?&pKfbLKpY)o($c_WHd_*kCI@s46qG)f2rET1qv##xB zM_m5w<#`f=LevZ*iVe4!xDr7(!R z3X7s;g|^YegRKd8=RQkFiiej}7g5dHY7h)7;h@%~r+OOH&mUrNPbUkJ%9rUJJV+)4 zA<7p3q5SppRzwtGtF|seis?l&xEu|n*Zjh=q+_rk zSgpS7(0lb6*K{=T&EHli$D#N1cx;6~m(jC%6$Iz)XGK6%eG)N;| zf*>W*L#K2|cL_)g-65rPck_+=%?wlP+W$96qKF0N`Zb)d z5b%0-bUP1ay**skmk*4=ws-0C66xq-fvno3<}UXMFH7L|xKDtHR3liDx$kLQg@0d! z@R54Q6eELY8*xidx_e4>Qks%d-H(XYozprh@?xa_+;SGBIMMWTnI!k9uMGPW$fZD3X=#xVB>3c+8cZ+A;w~8)q z$a|de_Nv*I57X$!sMqs%VQzQYJ6qlpK8D4$QJJQl?a%qp;Vq5!AK$7_9L!KVwouAp zA9in2zwK#Wq3$k0!>YEIR`cjI`f-uXbzf<9D9C{z;OS zM?lWZeIV4`=Rov>ue%;tDi2?GvLcMm9A%{90SN_}#Kc$&;V}a*Vc*YC@3@_o@+urJXkJwTiMyMS zpmE-2MUbm`8C7DI(XTt#ckWU^=cwU3`5)4`#=^gM9pamsoG92O zbC~bDl$uqiJZ*Ur2`&*|1zV08FyX~_^T|g8xF%_;x{BaL)YQ+diCwpapa#gBS2?TY z$R0{$$7&1w$-eLV+K(_AwO0Tu%_8(EMd4*DZ$m`3;L_)rs>-!x@}F2=zsDd)F`E@2WXLWCiZQ`M50ew+p)<&xeTy&CoWe{ug&7{~UpX*9 zmEbdToS)8+LlUIb5Y-aQiJqkZmP{zd^;pfbN6&9(8W;IT7gm$yivM4iVCj>j&9RXY&HiK zwiJ~~p&d$XxU!O)R+O_e*bv#gY3I|XvD?*j%2>hvfXJ8TZ4lwQ!5LyM!;WUFr?`6Z zX$h{g*X8Obb|re=-t&09!$G~qSwjwR37 zeHO(-R~Y%P%aKhXMJCW$TrUz;nmp76!Zz37jEfyz5yYo1gGdK&*Z(6HykToH`+irD zdrfcnXbw#(bN+o+)=Cs^z;aj|=fSsNM+_&^<Ye2i$Lb^jb>(D9}cSjP&Vf=g;8S|(M=(~hfzzXm5Zl7+sv`R zSz)cVToymkt*5}rI~bUpSw1yv0lAmInJxrg;)Hxua4*d~`Ywb+rL8APx4=hD?M=5& zTW-Hx2Y+3JI;x1k^F=FcM;_mx~Sox{`QiU&ivb!X~q{){*~!-FI&@ z%Lf+n<|Q|qre_F#&JS6V$U(nUX(dHr)a(fY3YhvUdSdz*v0ZzboU^uYU)K5QEVv2{ zFG>XaIRxXi&9O%xR3W-r<=Y7;VE7-^Ox(B8-hc<%RtK-)MP!XWoI4}G`$A@t>^S*> z)X6s+(aQA+A$AezY+S)9g*t39&WN6RCCW4_@pdjI$~K!!pEq5EH}>iDUyni!0H%4F zr)NEcaWwYeRLgi>BOmE4Us22r1M^z8K1RzJwKh=OsJ|Wu6l;GNF-m9Gvnq4V@%s2# znP}ghantn7)S>OuB-Z__;C63?LMP!=TdL{KT;(kv=7o)ngc)eTIKgUnlA7RMgAPc+P^pxls!otr1%)Ev*KVdp5Ni1~6^ zl8%r#u3i69Eak@Ap40)tD$j}XIZpmm0*5H8rEsrclrVep3JH7X5g|fO<{6@3U)qWrDze$=fMtbgRw-hNz{M4PdoT62N$9YFc)T+Mmdq>i4h@hp&-sK#zu|`{A@w5kFo0#%BDE zR_6jBFefgfw%Bs*dk}zmFjy2*r0aVK9P#*asUx{Lv+$(6-fG9pEZ)Z@1EpJ=Aa$xQ zBn>GaZi8-9_?29*1h@f2X z*`(rN3PnznF(5gc`qNcmoSkL;RGh$SLM(3oQ8YFv6K*U#43Vi&OufjNBfW|sM8YUa zv*wXWj*CC4jaZJ!@WTc^>)Kept)Sqq8ARk@4#u)2N;KYpeYZRrTN5MU;=R~mJ$a3;VJS)|#JfN>zbp?(kYh7eeXTws zZB=r(++w~toSM_`OU2~je?_B=&DVL|0$=e4U|sQdU)T;&Y$3__7`u!LNiWvf}j(|pGwVma!e~xc_l~zx2---rsHCQ z;x{tGx7#$E^#h%sV{~;Ump>l(waI7s0mK)%Yyc}s_c(9Hj~+^;KS1e8tPxjE?wouV zE>c#u(3SQrJGTiC!WsljR+*65GOr5&pXXDnAj-BC<8gjuGsTrX-o6fxt1Uh6dARF= zA>v6L>Sp$ZWg!r|^Eq%8r$TQid)`gu^MA8s*w&Du*8>Tmb=(QJQ)Th|Sc(MQ9Vg{T zHTkZ7B0WJ@ALfoNVofMxaNQS3xGm><<6qO}a+lb zf%#?I9bHOsGN$>wO*u#dg!Pj|iVHs~Y5>L&#s>lzA!AdJ+zN?_QNo}!G9DiF$hMc* z0aS9x@{-&=a7T(EuhnaI_^)SIP|uczoIJWZOvS$fTG}7*DFQU&;5JRstTU7E%Rc+p zmxul=R2+Y*mqlMi4UEfS`^5f(l*~l{Tdz^{;1z)|*uM3eJ>ISKMB=mz#SWAjx}4{#`Q%Zg$}b*8?0J`A zOT<`SS2;zJBUp)4-&xGhH*BVlGeu7VrSu*w%$Z#nnp4{*`lFy9X#3y6;_%7sRo`95 z*`7t%6hgGcD8jY`?pDx#w;u?X@NjAa(;VMF@Bey@@@wUgcDahzS%3WQt>N2 z)m_LbR!9@0(qQPV-P(#O;iU|r)9EaW;OSZ2aoH_?RYCj2vJ%`!>m9aS5UsnCBhi>a z5HYDW0GDh&I$?I-`vrWeAty5bdMi%*H1dvEf}geY;C$DM*}-TG^xX1N*|pijj_jkS zg0)g&?X@oro96w)vre|J2}$~mqvIz_kcjScL!GgHvLzDTm+p1e6jR;%kahbVE2Kks zN#(*CowW3h)B0ZR-4zFVmQWuuSgi?(Q5-6v!C7f^?N^qVYgFEnB)PC6`?`>B#@gT{ z)3w)@hD6GCo-M{Ve!ArKHBw2XUX0565j34nI_pklD1??dpt5>@dbCdUAGe-64Iqs| z%D2t@8|;u|?`b!3H|hI@A{7_ul+({9PG3+ierTirGS5iKXyfJ4fJZCu*vAhdtPYtn zlq2e5P@cK;5tg6 zveOh~c_XD%mffAO=m0O)_WPhhQ|`j_hx17lc?E@_$(@9N`gwbo1k3B=wPv~pL2+JE zrhw0#W)S!h2E4JdME`#N71qUudc0OU7S_Ar@Tsp3Pz0{CMiI^r=U*9Qm@!r-*Uw z^Z|a&siBcp>j_yl}{OC<1Uz zcwu1mtykFgh|&)n(9yJqGM?IjG;&xFF_H^ z(G%V`*afbs%;(8@?6Dv6cwA~X(SB*|YvV_az;JhUe7TeJeIkisZ@-NKocJYZGVZ*E zdciRx_b}RxbSlfvE?hJuvb~%0c%9q55SU{Hzaiq$?>P}1uv%kteUgfma{A*Xh5jYK z@+g`;M^io#8>?MU4X|oeVg~a&9>(LB0>Ya}Je4-gcaO*`BgSi)j(yesOx)*{@#DpZ zaJ3Z|gyLRR0%0B`u^2nL&W34=rlBj^$MSC!V#`-h=$uc-b{Z?78?~=2rQrU>5t(J% zP=v#YPN`9=afMOpBt$+~{W!D=>LD~*GUB>B*3sUfpp4@AR7+*qyV|5okJYgDc7oQ} zO$>jWT+N40@&%mI2KL}6z=#ZyN*nH_SAa7Wyy&Q}t?X9kBE zL}*tOHbxTSp)=`YehsA$RD7BaG?yT8vh->IM7!?P{?HLA$JL514!JNt>iaIxZ$4JD zcQWak#etqyTE!c7@@!iNbt98}WK|atp#;EpF`|*UkNdDecTlQS#((M7x#{H=e zSI+d=_Cy(--s}Uxg9KU7aE5^F`C8OS@D`{n7=olU@n` zv8aP<8}&?cks3$(CHzX+nT4b4O6q?d0Iabo901q^zC2>sO+(H$VGGg1mzDi-TCZgk zf7fM(%_%*+6;;QEBp9K0y>X#D!Q3dmGkaNHM3%$DY~O|H@g(E*JI)4aY>E};X9Kv! zMxz%zd9(B)n~pB#{oI^i*=|($z6w1EzUzy8BeJ|LF%%TwYZtfj#b{Lz>C<5 zfzz^KKG?NXBE)LTd?MJmxgLKaSV9f}>Yp6!``o^j$Q-ssO5!=wz3^Onkg{FYjmxCF zAt3CewO)UI!kuX>I~nOh+;g?Rcb>cQ?Ne1@XFmGzNJ+*~wU=0?8TDeg%HH~LPC4XC zjIm2;k~5*&os7B#xAPH~{a3YD;R-b1;opz-W6Ahua1hz%?=N_v>kA~ex@^NT1ZJDw z4k$IxT`67UDvzDf6X@$TP4X4s+@yN~0~E(-`3niu;?5Wygy+5Ta5m_xyml~Z?)938 z7*sVliOvCG!9G7kC+68asq7wVu#ALBY}TqtkRIT z!A$<+@NL}WJfb~XYlJs!40m{|<>c^H2&2~n3 z32)S?!ULpEdYPb1OJLuz`3qFVh=*oNAdGXzwOY8f%i_Q&ZWxN2<=MKiwweMd{wkh*N2|bCozgxlop8#5m!`K7AR}Xf z*4qbzL9u2{nAGx{khEWndpja;O~Uu#c{Swp+xb9q1gyCSDKJ64zvXDXyN*bmxK?y| zWMU7wS^qbYUtq>76<;{~9|jzp*G%^t&kEaI0j_#htH(=($8Q=CqEuLlbtY)3+qZrq zbuhKzUV_Ws1I6+~{e2LS>q=v!e6;#oE94f+W!l=6hjmC>W`6F^$OsCxqK&ZKfmX~# z{H;~0qa7p{(|%Upn7=nM+N;y0S{&t#?T?zU*f)q#r%T(o9`?NX?+k_?b3>K37EYnd zL+ZK%Dq1`zF}!gV!1tiQ*}A&R4;^BXm_P1hQry*Pg9{g39Eytd|GM5niY8Fya!(mU!t&rn+&3W}aC9zi3uVs3CkEv}1y4|I8NC z;T|}m#;G8r1*f)g**8Y=%X|TtO&^2b$&kyjoA5PTRxwt<-U?G%DZvfoelEjn1&li* z1tb%=gV<0(Yv=8#*=PmnslP@`>G@{}Ap8yxiegd$$MnKU-KuuR_2ipt+5Lw{k8~=I z7tnM`{VgobL@Ykf4zj>hf$p?7gXESUlZFG-^+Y0>qvv#7AL>r+C?gY*;mkHhhF1tu zgl_WSndnpIC%0VYMh@7=8vPF+kY>Vy7tVw-35=)O4LP$zh45g1rkdjE)Upy0I5IQGg$*KbYIZZS>B`Z3U7N z`H-EiETEjPf7&WrxyT@Z@V(M9=C)3!uRh@Jb4r$vs$&CFMfFXq(K`eC@!zL*e}~^g z5y@cWKPmtSG9`k@d?}4=8sH6Pfq2ABFD(o0M13$`d9+MPN|Cz_?{L^!WV%j3XNY>L zcvD`g>TI>gO&x#=amk`Tm4$KPMP>ZR#f^oC(T-h`dP~CPK%Xjxg@bL9uD;2NntpgC zRTdm7yfjnCl?`R@71` z6CYTl*|O|{6-`DUtsx^BBaa`SQP%jv<=X%F1R0(L&m4c8A}+YzHMaY(I&Otn6rr~ldLa&zNa2#jk?C}plUV;CH{@B) zm41k_#ykGubF}tzAl|vG>|su1AL$LkNGiZK-#9gTI>FJh>*0+@8%0q8ELyC>%2jhh z)O0Dk??5*({MCxt2bxX_>>8#)Gi&EInYBW;zyBI@PK31Q?`b{D?Rz&P9CaiF5GkDM z3j$6%Yv>(kWx6u)5dUB$9%vC@>h9=(kJ4XQ|33F1$n!1MwF6+~KhkyXNt|aj*(L8^ zi*V`qBZ+C1FkSh+idr10|8df}gMLH9GA`O-ucTI#jwdwkY^upO`ac*A*tDJ*1sxJ> zJIyE{aW-#Day8RTlt0e&2eY|85;@-~d;MxAYpOB?0OCH*I44k6UeUMbvt9+|Cb!XE?WXlH{cYlP%JRKBS40n)(Fo?_^p-mDa)H+1d(yZZD32OoE&wz~I#?N}xm9wIz< zN(lbTtcJ(BHXIuWk%YWS1h)3&cVWm2Hik(X8L^|>B$r4*U5uq7(M%w z1>Ga>7{D{(Vrm56$oHy6%5kI2O@cQpP$do9QlsmYVE2Tm432PIe^DL?%BM!=ZVRc; zlyyWOq!BS3 zZax~VeVHnB5(3~|Y$2~|w!_jH*=L{Pdsw;=I`=bWM;2`%qBGKu?1@}hzt2T6(R-v3 zH8UUR!LxWH%EEsA#KwM|^`0pY=n+#M1#EMaTnP4`Fk8{2brq;|r#89vFzGh^xi3gD zYZvBuXBX68zkX%pB+SOZQxBE^V@Ds{mmJ({fBFN%O`yweYA%C=Mn3eA+YXMC5_T4; z=Rj;G@)Bg4!@t&kI*ipn44$WonF=TB%cVR`k9PFsg9ZuQ=^med85CzzY~U#{lF@Co z%Ol$jZJQgCb))vluTfBbd*_^q-=gAe>V9n+Kl3@)+F05R(IVVC6XPbF$g9=A_Hmx& zOD0;8*CsE%vf;5pHUfpLoa?@Fk^7`P zL$E2ITC|1w!;`A|YI^OefFg$f7QSFd1@fl9Ard+9)rbBAws9em7AuLU5aO=dJLYFr zdit<%M9o#Aj!>R{L@d;%u3OMLN3`DT)%@?6aM4#BkT$+Gdd?5m{h$Y{1dT!ybj@da zKapB(}V!>g)D|6Q{%+W_PonYX*;w8rcUTzboq9& zYrT{)YeODaXfTAM>j$zYl^m%v=sdNDj=bgVvs1_Tna(qF56=3f9CGa5&T+{3A01O5 zZZTofzzCI5<=w_pS9NHO@b7hrqsOT#A)cDjUlg)qWIL@NI5u4_A{|9MNo;@t-Cb|7 zc;;ZpI>{N(fO_KH78BXeq){tb5+|2%c)1Q<6FE`Ab&N%k{?eQGvd=^I)A&C;2eVa1IiPvTwTo9h~Bj;F$gW-`X z-LE&C&whWZ;aa;OL0DlF%ISQ!KV?p&O1j_MSl@Ta3qLr;->uC<0{<8U5CG)z>ON zn0Zm$^rZ07=|6ZJ61yX#>8cgrXxU4>#B)&8NCoFbdY3vs?Gb!PyCC)(+fT$K{9vyycTfhPM*~@ zC-z?>;GoLJh0$_)N0}thn01vKS$Iy!+UOPOW7`oOj8Bx*x-;O$;ebtBxn;jeq2OfH zj^_~p7y9+LX_cV4MrquGb=Ljkv`z+*Q{TfiHLiCEel_Qf#9q3y14bLd!>_k@J?RBL z$FN2`SSczRC4&L!JzN=La1BtMRgd8tf8$t$5Bykt^-Y!LR|{FIE_$4qLRe7_d;8i| z4)YDf(|^!`B@}Q_c0>rC#?TMNaevq4;AR@Xr%1`xw zv89g0qG);%u`8?|GMlMSFXIj^9$#ZLGia{mWe4p{4!VdWQ$5p!%k$!7r?2167oE*+ zyUG1H@|QsOkMBMcZ>&F7m8Y9#nV1_rQ#&Rt+Kq8Tq_)OHlFGeHv|IR$rdB>8j;Amz zOd}qhSxJqihu|VT?g*E)ja^v2h}lPhUDeZfBJXRkXNz`G>WKrVOtS{G720%Hj1f;4m5T#xN3M7dC)9ut zjd)~KJ?mUyTUAx|dv!WH`uasIiy|8Bw^Sb>Uhf@wQO_z1#j;+%Kqt zk>RZ*bdn6BOpKa-v6rH28iM9i-&L&dKkFCnJ|7DAK7hV+4h#+WM>1j%WlV!^EFt)t zWg0C^*n#pwewyR9PGT=d(%98}LK1wOl2pv-fXHyHGcvPUOVEQ{4yJND&m0mNB zy+Y=oJ|}MYXfj)fTNGZoJfAEIBw;C+qL8`MWGdgeP6?|;rj|Ko)1dHsLZgPdbgewK z-vE|yH=oGuUL?;Y2@SE>I=(Jl9&%|?@i+~;ukpSR9qoU#OaRevu`r@<%~#~s?}K-& zqJ<%}L}%=pHfm&@3YuTQNU_s$yi*w7>=w0ANNB}fXVMXu`L>e_dWE|&<8rou*>Rp} z0ZcaCt?6FjnxnO985qY)LNOZj_6s^=@i_3bN z7gCb6Tg$)fb#eU68(hLIY*F;1w4emLut5Am35y+gEA$#(y9B!(P-#c{PRU+lK{8kY zKH(ss=A`n^sXH}|8)OIzj2b^l{)OFGFWZrFrBV6^;{C#7^9(Rc_81_VN>y){jxgv< z+Q)E+Adh1=@8h7f^rt9cc~k`HR~I+0r;f|XUjz^q6zAvQa;E( zvtxrgzeI^#lJ!{x;ym&EJ23|t#w-%*aO9=!rcCh5uxv;f%FEs`I}DP*BeJVc<5EtK zmdsj9(#v2ltWAN`Ef5*d$E;VF@I3VLA+&)qXAouyxz1>oHx+N9!c~W+wi~)?L2*V& zb1!zx82N?&OIA;5T`>(0E{f^1tSM(7qE0HRyy@t&6rBPWpIIm6g6C~h$rD z&67;yWS;(7Br*Hg66|Fdo9P>Q^9h4%(>R1DfzIF#Kf6AR3BberDD?UjCvWEc)cby) zlw)h{y!{U?%#pVrL`sPsfPE>r5Cb}XvZ`<@_lQg1p`th%zqUvP1p0B~w-ZqXZ>-)# z#VTZ9Z5<9aX59|Iw-Ogv_p(toy6t0w#*q$el#$f+4J7dVF(hUHR0g&T4e|cNwFR3h zvdEC#COGh~NO)_OJ_Hbqt~=y9@%OpE-(K8l9*mR&ORY=EKd{iu3lUQ4iU5JdWBocS z5r1!9uq%-omiax{5agdOOMfL5$b$k3-F55xpT~)kA8JmKKyMs4|0*BAip?E5@JJgJ z86)ue!do*h8v8$@G=n1}Mk#VqfwVhLS}^Gfb-5bb>5}B@>3=KqwNfUMsO@!U7~C&B z>-AkX#{IJ!ihJ+BmZPRIpV+_Q{7qhf^%VQJa?MRe-oVHR)o3N^zqK`l@##3e#+@Ba z0A>T_&Eeb=3Lz^;TOOzXtQ?xizs_!2g;NGf{R>sG;9<_$Pl!h!^B<1j->n7z!pi{e z-lpQgL%JQSNcj$wl+f*L{`MAgiM@ihh%zk69hWQbRh*KK>O@BP0W%OaL?bGuiWwlzLqmOY*-Itf+8w?0)AiDXiw zlY&+lwuqm;TAz*hJ+iP%QP~Wu-Ro>qt0^DSOP&FAYSz_7g!szY1gp|{8=cwx(`9PC zoYj{jNS5ei8NeDzbpj~-X)r`T#s8meTCKN|*~aolfsgg8B^*q|3-++6*y=@*vmizP zHQ(<~0oYZPo4o8OQ4yS~Gz-Q((HRb3FAcDo6&M&ny>dE*vMw6Aqvu%}!+b8T_N%AigG4r?MDZ$8?UPPf*3pP`h{x2{y_ovEa)Oq`x2G3?kvoB2AbM*?<@{anq;hgh{L zx<=ps7CGB_HB~*+(y)bKQPK;SiU;Onjg(YZEgDd%mBr%s>H-^mlG^GatZ3Yck{LgBO6(f=sV_s;^{!L@$gTOIiOe($kbRRzNPU;f zBZKt2#}&zJ*Wt;cX)l)rimv}reEG`1uJ#F#aW@rb_4r@ zb>(Uofsl2@wV{#7pYe|hn(=x}{bd^iHH|u>NuekJ3@VBcKw>AFlMYGK36vu4hE?;j zzO-bjeed>erO4eiJ^>NUmMkGW?W!n1y(eEdpn0naqOH|@fbC3qFZx$HFRvn~^cRAK z%Pa0kBwk{yu>{1aZwUP3tBgdeKl|^IBH%#)W|rj(jp#J8+6s2bynNWUxh{ue><+o;xwOtmX`XM+Tc0CJyrv0cKHYI!^eU zKgW>Hf5D999k?z`v$EsqLcfIHHUNge_0+U`+*y{{NlPpI;bq?aB<-zV$I7~FB-6^Y zmHE498)3%QZ^0?8l7&+yzgLkLIWVcb2x7Pq*Vxfp+3EMZmA@JQG!P**6zDhv2Kh|^&vy@x)-aR|5$4Rl#<#({Qm1sF67=Qtv zZKw5oJzfQ|wbd&=#J){3DI9cfknj!>Yu9F*gu<*vnm3`Q4zyqJFv3M z?T&9&!|t6!K}%+{G9XP+VCH5t>wW3eQ=m2RYrVv zKMbbs^AW)w9bM23t!e#&uM)5Q@KUZ4TMv+IH-#-uC90L;`d8@EkUqv!%+Qubohpd2 zLp9#G@%eUwY1IHUo#}7m%utepUn*61E5RI?RUZxJ7mdKEn^!OY3Q#5!J_AOvCh&a% z&PwKaSE7fW6aHxz>?tv6X9CIh1pRDaw+1K}`yUNSC!k((BL`77kfZ8f+0SOo;Nf0$ z5F+`X|FeO#BT8L23e?&?DD>eE*H2U1N&_^PB5>-!{oo_{HLJ+rkTe)BU=PF8nUD z-19Kd(=(91!~`@0nfjju@_qt<&r0`(=6(OH)hWFf=XbM3euSEmM^y8F+w=MJiW57X z+L_-L&z;dUo6i69Ms4CvdVe~bjj@%rtKojx&HuzHwv4^2_6YWpE}blE@~EzlCvqvM zjpd)D|C`?BK?s?C$X|tUZAIL|^g3E`vcnEn`uSXG?Dig@U)I^^bTinbeW!iof-?WF zPuRbC4Xo(1>f8WiNL4n5i7#r+HM3Tp1fU;Bq(jf}Qz$!u0N7gYoLhLW3f?zk06;$* zjm}hHdZsYS27v2OMi?R01D~oy2W;OU)W#6168Eia2sSbR+|OM)J2YlX7-l*w^pW58 zp#vJ(3hd(XtO^FsKPxQ>#8@4V_IXoz^<&{B_D!Fqv5^n}u&$#lf{hJE z1sV7@858Y6=gc1s@TpTQ(@Qzcf>C1=zmC?DuzNc`!_)?Nv_N-sTwH~Fa8+@sU2EJ~ zTq#jpkPcKmw>lPX(B7jE=qq{mmVGcm7N`5iNj2!{XRc9c=faV1a|@rBMx?OIEi=B6 zBLbj7a6N@6QL^62xU*8N+3MCd_U*wSNj0&J0cdc$oZ{+cOwQ{x6OsvRIc1RUcn|S{ zhegidwK)R-K9bj5%5MMRvi|G#l?kJ#Vi0!6s;Vj#Xaj$nti*n7w^(& zXXEp_SZR#sIwt@IwT4&;?nnGWPin_s&mekv*cB43{Jg!lhDJ)wZI*T^1G^OJOs0rY z(l;F6Fg&L`rEoEEev+`b_0QtsV&m@G6lEMYphg*OFVN}G<8A&O*up;CY zHlcwSr82bFn{f`k+PD=y)w)_&l?8?N-h6vvrA_F**BiL=(Sn^#A25C{bK zR`Ndrv~4|`{h@5!+f)}D@3JFN3QUF2CCt}I(HXllp|oAKtz6C!nb6WvuLEs zzzlV}GL%~RCSX2Yz|O}5n%?OS)`IPtQC6nDIIUo2oi>R24#PLj(8#Fu8h`vDwPQ-o zu5jfETBkqd-gm)_^2qdMVx@XEa-4P&1yQaX-7zat=(O)w*?PD?VvNPjKJy~2@~N8N zNl`_RR7WT5<`)(g3KByP=4h(1d3>c3FuoLRQjvAjA-hwd+g(POjdJ5MM^ti7#+^W? zlBL{B{4dNdeCVub4A`7s5B`_?%})V4m}7j;C(JnysM8K+n)c>1Te(v!y?qB)|J{*H z@vd-j9-ps2*Q{W4AX{P6dbm~NOtj`SyUGBfQrmCWmBE}To{fbxD|9-{~R0~)XN`Sa26P4gpvFeg2-qlfRxRr zO^#|#U9AjidTC%{l73yzu`X@urbQ|>TLf|9V_40asdAUM`C4KvPKpwGmq)cuQx(d8 z`dh%En_%Y~j?y6Xlf{B!`@3PEs0()MFOGy~=g}GTs&k(?+z8%XDfX&OTXokN2`}uO z7*4E}v1-`rKrc;==HZoXoq>~NRa#cVS5tU(9gSQ_0czGb=y%)BJc&Tb zgN_nZTxnMM!)=$5of7-Yr<`Z0drx<_6N)T2^{ zgrt2!-l5=W<`s_uFfju#cepIri%`bAX1hckMPG^dnmsR}?^8Nn0Ps9% zGB(zu(KeSkT^{N&i*A+pzdRO|{4f=%tmkI>x(_89$u)#v4`>n)-z5uB}u-E*SJP3sjAk`D72GHj~sV7J%lK4%rcA1lh_Ju#;@;c zx6W|h9p0xwr!}}uV=tdFmgJQ4nq)(e0)~@>*qGu4pDLv5w|8~vey%QCySH;Qhh(Re z9lhwy4kELo*Vmjuuy59hC+5RH_Gt*F@Zx4fEd*x|2o+QHWr z9^d6~ZVZ6;y|GT`P1$8G`?M+rqWLclYqg=^dVt+aDOWU-#3<415D@Bq$87G%76g5Ela~_-b_hRFx_9-eaAHXAC7{u`(zH5LESv$YFP^MBg*1Ma>j*&E zP_c&B)2H|RSY9DA`=pB5L0kRO352qUZ=u~6>t_-`vI=v@;4%3!#f7VznUX5_S(a`2ad!BGTA7B50j~J?mmmwQRcn z*1`K5Nn%In5Yt_fJ_gV)B^eByTm6&Q()Gn#%5X>s-8r%%1fZ-Shc?e%G6T3`W-*X( zRnWV?tLwQe;W2Jp>P4r0hb~$4R&p}8cJGcxDpHFbyzd#U4j1NEvRBAt zxfbN0cKifbPzaCiS5#FkZ)B8I$TloDJPw2|;_)nm#o-@+sTQO$fd&I?E_d+9rNfQJ zV_|OG=mp03X=)0o-p&!=?daLFZ9j}-HYvx#prQ~YqyKkbh?Lk+t)xgLP%klrRSccL zawmxuH!{|W#hwf21xosmX<)PU)P<@Gm^y%Wg^XZI3XW}kyHCYTT>wD-6nf#sAo{)^ z%Y7HwGq2SdfY93u8jfPR{}K*VPjz~pVPo=1qXWZ`kx6t%?sdMPvk7w^9v;SizX1&f zW`uV?8gZgy7N3=o3VVv#uV35NIgg6y*Ze0+#WU+MV!G|#O6<^J-vX@tbRS`f%c}d$ zbfA|7;cX_^X{RU{J?n7>8r&k4MS=a5nC>5Q4-(lx)hi8#*@&AsqeD6{(J9Ce3qTFy z|N9>3!v_pfkQSdIT;#x5_>ueR9wcipaQ2~aFAJS+6A%s-W`5Vf$^#x9(S$i@sb4r^ zcL-PDLNB~m8R&qz5Ek#dvuSjG4>(+iDCbgJs9D^lFU!m>}Iv1*V>DRFtkiXI5fsvbX5v+lp(#?yHUW~wvDZ&W5W^LD-y zg^Ff#;<46{z*Y9lFeC_)O0W-SdLj-jlO#sI$9t(>2*cR&u?pMsL z3rw)FpT$@0=1H75e*8F$@D$yo8|!R{NDb|ae|t&|1LNn42_0xeNh8}=Q5fi4Pr%sy zO5!Fw={)r8rtutw9!NO3P_M;;`aowa-Y3o@|nT$W}DV~U$oWQ);e z1sd9`Ay{&!W9U}k$l>|~EQOUsR>C4e|E8Oa|+;`vrSUF3|s}I~)YD-AgRY9JDtY=`i zp4)x;v3=+K>t5oogdS8-y@G$9*px3Zbk?o@qph(Y98nO)7wHX{MSq#q)?jl4cEjkb ziCa!S;&`bFTb-a6H}fH)05(^N8?A+xbvv)g1Hw#|c+pV1`ZHI&jxx;=n=x1#R_&MP zIXF0^Xp^U)8|M3AttJ}EX^2U;QwV+9CKY0ysce&9D%9ZTR3I@hEe^90}VBLqkKx%qyoH zV1L#wO>ZEq^+)Qek$7@lVDmJ|&sh6X#K*I2OcBtnul2=YD;|X~Se7mwfp~=eyNgCF ziU3=gD;&y``?BvFg3$b~o_k+mIoDzqH86=|=6MP8YM#X3o@E!AKv?;h57uI(+$m+R zo$fRoGq7`JAq}krqrq9;-{$TYcd;mY|-ylRkkPUvXcqfUkTQ^H!pIPRUD23ZO?JMQ>w;Z@AO4q$e0BE!b8Q#T%hb5U}@WXN3ZV^a%9mhZ@23xya| zOUUe_j79JLR%pG;&cm>QUTJet2x^3Lo7)5TwcdbDRO&J8L=f7{NB)YcA5@t3E=mbj z)z~jBA(R>~!xT4~d2xww#-}+QGbQClA*97lXud?CTYMmV=}LF?iiB=bC{O=-N_~+d z>hk5RsH(~9h|9?n*~)&01P*7Y(}}?K=G~SS547hQ3+7MlcfOA<=3ZaNl+F+Kx(`s* zGPtx}6)t9a3ffrW#GwlwvwAsdefX)2PIZl8tu+_2bh1=M=&%N?G`yi5kZ35@8aPZi z2U1q+XshW{NY!gPt_8%x?js3%ezBZVQ&T&ffK$SuLtn!$O&a~y2Pz-cZ){gh$D%CX zsB^cw(f5t7=JhDjjCvH|)*+;ju$Xu};-h-#hsHIzK4f%exe}(rCnk$GAn%-xv>Qn@ z@<;P}$hwV#g|Bnp1Z2wIi^A2lQdiw+Ju!_vEw@ktn#gidr{x`dS;KmXo#W;zYmkfE zmk;EF%au(VDCl^FA~;e@hz@Kvsvpd9+5~Kmn1}L?F8^t9+M{+znY(@Mmq;B0?QkZs z-rstwPUSOGgDvc8W*^FZrYT^P(;Bn)y-utC$Gv%FM@e=>X4z9S-`nFQ&S{6T6;_Qq zqkgTFsZi8U7}qXD=wnLe%)xxAUc0^`&I*;&TWy~bozA7OoNM7jlWRt7PNqyAO{D{H zk_oN43ys>wosgJF`}b&+d!pF-wxEQ!Pm&Wcxi6f7i^bUWcZ&=i*|b9jLyj}YNhz1K z+<+^d9I2j!X*jYDRQ`rKosuqNl5W6YFdkXs-*h`EXe%OUHj2+Jn4~E@u&!+7`LxfG+2B@^`%pW5{wJjIZ|bich400-Zgtn$DY5Opy*eeUMM zl+n5tpY6*wd)*?|Ex~&;7A&x6N*^sZ=H}*z)OyJ9?2pZM$m1XahG$Mz*1_$Fkh zk`zPyaE1G^?4p!mXs4^iT!L9jVqi2&hLMEfARk!F;Lvzv9aMXystJ0Rsc#Td= zlUFtb6E?oP-%h>JYNvLgzPhITLh-+v!jcnuxDTz8e`om_{7GtB;D-y1u#kIViY^m+ zCR<;0B_^2m<9fX*i-K3EnzN-k<-N5{v79~B9E0bjf1D&y~XTwDV0luTg7dsoH`t`Zh` z$GW)4^tkq$QH`!MviRCQ^+t3|R^)Eq6Z8OJ}+GORWHTnT_ExzeDUwr6tT=??35i z=D8GEe32Pq#Tl}vjOp07EE$_P(DF#Uj{z9Fg^USK5z-nhl7(Tyzq7oPRL1>?5#qd| zLca0LO;JdH_#Xh7>{;|LBEkC6Ux2%{_&l%opE@IG41TJhUXd7 z>9!Ru`HwrpePslOfPL5XQjQL4AU)r4CA`mSBPsgq-7$a_mPRfeCFuLF!31Na84lpK!|_t zm2aN4@d+^qZHAdWswCpp+_T=paZ=Z{|0)iJzLp=JB*KUmvhPyhe` diff --git a/Signal/Lottie/NotificationPermission/images/notificationPermission2.png b/Signal/Lottie/NotificationPermission/images/notificationPermission2.png deleted file mode 100644 index 7d3fe8a54d002e8d103a571efdce0dd15ebc90cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6030 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 diff --git a/Signal/Lottie/NotificationPermission/notificationPermission.json b/Signal/Lottie/NotificationPermission/notificationPermission.json deleted file mode 100644 index 9d2fec853a..0000000000 --- a/Signal/Lottie/NotificationPermission/notificationPermission.json +++ /dev/null @@ -1 +0,0 @@ -{"v":"5.5.2","fr":60,"ip":0,"op":180,"w":1080,"h":600,"nm":"Comp 1","ddd":0,"assets":[{"id":"image_0","w":820,"h":232,"u":"images/","p":"notificationPermission0.png","e":0},{"id":"image_1","w":820,"h":232,"u":"images/","p":"notificationPermission1.png","e":0},{"id":"image_2","w":820,"h":232,"u":"images/","p":"notificationPermission2.png","e":0}],"layers":[{"ddd":0,"ind":1,"ty":2,"nm":"Signal Message/notifications.ai","cl":"ai","refId":"image_0","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":107,"s":[0]},{"t":116,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[540,110,0],"ix":2},"a":{"a":0,"k":[410,116,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.605,0.605,0.333],"y":[0.517,0.517,0]},"t":107,"s":[87,87,100]},{"i":{"x":[0.534,0.534,0.667],"y":[0.566,0.566,1]},"o":{"x":[0.351,0.351,0.333],"y":[2.108,2.108,0]},"t":116,"s":[101,101,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.448,0.448,0.333],"y":[-0.816,-0.816,0]},"t":125,"s":[99.5,99.5,100]},{"t":131,"s":[100,100,100]}],"ix":6}},"ao":0,"ip":0,"op":186,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":2,"nm":"Message 2/notifications.ai","cl":"ai","refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":64,"s":[540,110,0],"to":[0,0,0],"ti":[0,0,0]},{"t":72,"s":[540,304,0]}],"ix":2},"a":{"a":0,"k":[410,116,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":187,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":2,"nm":"Message 3/notifications.ai","cl":"ai","refId":"image_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":64,"s":[540,308,0],"to":[0,0,0],"ti":[0,0,0]},{"t":72,"s":[540,502,0]}],"ix":2},"a":{"a":0,"k":[410,116,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":185,"st":0,"bm":0}],"markers":[]} diff --git a/Signal/Registration/RegistrationCoordinatorImpl.swift b/Signal/Registration/RegistrationCoordinatorImpl.swift index 0380dd1228..05abed8a22 100644 --- a/Signal/Registration/RegistrationCoordinatorImpl.swift +++ b/Signal/Registration/RegistrationCoordinatorImpl.swift @@ -1231,7 +1231,7 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator { if inMemoryState.needsSomePermissions { // This class is only used for primary device registration // which always needs contacts permissions. - return .value(.permissions(RegistrationPermissionsState(shouldRequestAccessToContacts: true))) + return .value(.permissions) } if inMemoryState.hasEnteredE164, let e164 = persistedState.e164 { return self.startSession(e164: e164) diff --git a/Signal/Registration/RegistrationStep.swift b/Signal/Registration/RegistrationStep.swift index ae77071025..c8db3e1007 100644 --- a/Signal/Registration/RegistrationStep.swift +++ b/Signal/Registration/RegistrationStep.swift @@ -10,7 +10,7 @@ public enum RegistrationStep: Equatable { // MARK: - Opening Steps case registrationSplash case changeNumberSplash - case permissions(RegistrationPermissionsState) + case permissions // MARK: - Actually registering diff --git a/Signal/Registration/UserInterface/RegistrationNavigationController.swift b/Signal/Registration/UserInterface/RegistrationNavigationController.swift index 726ee81f6a..a09c09c221 100644 --- a/Signal/Registration/UserInterface/RegistrationNavigationController.swift +++ b/Signal/Registration/UserInterface/RegistrationNavigationController.swift @@ -144,12 +144,10 @@ public class RegistrationNavigationController: OWSNavigationController { // No state to update. update: nil ) - case .permissions(let state): + case .permissions: return Controller( type: RegistrationPermissionsViewController.self, - make: { presenter in - return RegistrationPermissionsViewController(state: state, presenter: presenter) - }, + make: RegistrationPermissionsViewController.init(presenter:), // The state never changes here. In theory we would build // state update support in the permissions controller, // but its overkill so we have not. @@ -419,11 +417,10 @@ extension RegistrationNavigationController: RegistrationConfimModeSwitchPresente extension RegistrationNavigationController: RegistrationChangeNumberSplashPresenter {} extension RegistrationNavigationController: RegistrationPermissionsPresenter { - - func requestPermissions() -> Guarantee { + func requestPermissions() async { let guarantee = coordinator.requestPermissions() pushNextController(guarantee, loadingMode: nil) - return guarantee.asVoid() + await guarantee.asVoid().awaitable() } } diff --git a/Signal/Registration/UserInterface/RegistrationPermissionsView.swift b/Signal/Registration/UserInterface/RegistrationPermissionsView.swift new file mode 100644 index 0000000000..2c7fe09124 --- /dev/null +++ b/Signal/Registration/UserInterface/RegistrationPermissionsView.swift @@ -0,0 +1,197 @@ +// +// Copyright 2023 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +// + +import SignalServiceKit +import SignalUI +import SwiftUI + +@MainActor +protocol RegistrationPermissionsPresenter { + func requestPermissions() async +} + +final class RegistrationPermissionsViewController: UIHostingController { + init(presenter: any RegistrationPermissionsPresenter) { + super.init(rootView: RegistrationPermissionsView(presenter: presenter)) + } + + @available(*, unavailable) + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +struct RegistrationPermissionsView: View { + var presenter: any RegistrationPermissionsPresenter + @State private var requestPermissions: RequestPermissionsTask? + + @Environment(\.horizontalSizeClass) private var horizontalSizeClass + @AccessibleLayoutMetric private var headerPadding = 16 + @AccessibleLayoutMetric private var headerSpacing = 12 + @AccessibleLayoutMetric(scale: 0.5) private var sectionSpacing = 64 + + var body: some View { + VStack { + VStack(spacing: headerSpacing) { + Text(OWSLocalizedString("ONBOARDING_PERMISSIONS_TITLE", comment: "Title of the 'onboarding permissions' view.")) + .font(.title.weight(.semibold)) + .lineLimit(1) + Text(OWSLocalizedString("ONBOARDING_PERMISSIONS_PREAMBLE", comment: "Preamble of the 'onboarding permissions' view.")) + .dynamicTypeSize(...DynamicTypeSize.accessibility1) + } + .multilineTextAlignment(.center) + .padding(.horizontal, headerPadding) + + ScrollView { + VStack { + Spacer(minLength: sectionSpacing) + .frame(maxHeight: $sectionSpacing.rawValue) + .layoutPriority(-1) + + VStack(alignment: .leading, spacing: 32) { + PermissionDescription { + Text(OWSLocalizedString("ONBOARDING_PERMISSIONS_NOTIFICATIONS_TITLE", comment: "Title introducing the 'Notifications' permission in the 'onboarding permissions' view.")) + } description: { + Text(OWSLocalizedString("ONBOARDING_PERMISSIONS_NOTIFICATIONS_DESCRIPTION", comment: "Description of the 'Notifications' permission in the 'onboarding permissions' view.")) + } icon: { + PermissionIcon(.bellRing) + } + + PermissionDescription { + Text(OWSLocalizedString("ONBOARDING_PERMISSIONS_CONTACTS_TITLE", comment: "Title introducing the 'Contacts' permission in the 'onboarding permissions' view.")) + } description: { + Text(OWSLocalizedString("ONBOARDING_PERMISSIONS_CONTACTS_DESCRIPTION", comment: "Description of the 'Contacts' permission in the 'onboarding permissions' view.")) + } icon: { + PermissionIcon(.personCircleLarge) + } + } + + Spacer(minLength: sectionSpacing) + .layoutPriority(-1) + + Button(CommonStrings.continueButton) { + requestPermissions = RequestPermissionsTask(presenter: presenter) + } + .buttonStyle(ContinueButtonStyle()) + .dynamicTypeSize(...DynamicTypeSize.accessibility2) + .frame(maxWidth: 400) + } + .padding(EdgeInsets(.layoutMarginsForRegistration(UIUserInterfaceSizeClass(horizontalSizeClass)))) + } + .scrollBounceBehaviorIfAvailable(.basedOnSize) + } + .foregroundStyle(Color.Signal.label, Color.Signal.secondaryLabel, Color.Signal.tertiaryLabel) + .dynamicTypeSize(...DynamicTypeSize.accessibility3) + .minimumScaleFactor(0.9) + .navigationBarBackButtonHidden() + .task($requestPermissions.animation()) + // FIXME: Forcing light mode for consistency with the rest of registration + .background(Color.Signal.background) + .environment(\.colorScheme, .light) + } +} + +private extension RegistrationPermissionsView { + struct ContinueButtonStyle: PrimitiveButtonStyle { + func makeBody(configuration: Configuration) -> some View { + Button(action: configuration.trigger) { + HStack { + Spacer() + configuration.label + .colorScheme(.dark) + .font(.headline) + Spacer() + } + .frame(minHeight: 32) + } + .buttonStyle(.borderedProminent) + } + } + + struct PermissionDescription: View { + var icon: Icon + var title: Title + var description: Description + + @Environment(\.dynamicTypeSize) private var dynamicTypeSize + + init(@ViewBuilder title: () -> Title, @ViewBuilder description: () -> Description, @ViewBuilder icon: () -> Icon) { + self.title = title() + self.description = description() + self.icon = icon() + } + + var body: some View { + if dynamicTypeSize.isAccessibilitySize { + VStack(alignment: .leading, spacing: 4) { + HStack(spacing: 16) { + icon + .frame(width: 36) + title + .font(.headline) + .lineLimit(1) + Spacer() + } + description + .font(.callout) + .foregroundStyle(.secondary) + } + } else { + HStack(alignment: .top, spacing: 16) { + icon + VStack(alignment: .leading, spacing: 4) { + title + .font(.headline) + .lineLimit(1) + description + .font(.callout) + .foregroundStyle(.secondary) + .layoutPriority(1) + } + Spacer() + } + } + } + } + + struct PermissionIcon: View { + var resource: ImageResource + + init(_ resource: ImageResource) { + self.resource = resource + } + + var body: some View { + Image(resource) + .resizable() + .aspectRatio(1, contentMode: .fit) + .frame(maxWidth: 48) + } + } + + struct RequestPermissionsTask: AsyncViewTask { + let id = UUID() + let presenter: any RegistrationPermissionsPresenter + + func perform() async { + await presenter.requestPermissions() + } + } +} + +#if DEBUG +private struct PreviewPermissionsPresenter: RegistrationPermissionsPresenter { + func requestPermissions() async { + try? await Task.sleep(nanoseconds: NSEC_PER_SEC) + } +} + +#Preview { + VStack { + Color.clear.frame(height: 44) + RegistrationPermissionsView(presenter: PreviewPermissionsPresenter()) + } +} +#endif diff --git a/Signal/Registration/UserInterface/RegistrationPermissionsViewController.swift b/Signal/Registration/UserInterface/RegistrationPermissionsViewController.swift deleted file mode 100644 index 15de63454d..0000000000 --- a/Signal/Registration/UserInterface/RegistrationPermissionsViewController.swift +++ /dev/null @@ -1,180 +0,0 @@ -// -// Copyright 2023 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only -// - -import Lottie -import SignalServiceKit -import SignalUI - -// MARK: - RegistrationPermissionsState - -public struct RegistrationPermissionsState: Equatable { - let shouldRequestAccessToContacts: Bool -} - -// MARK: - RegistrationPermissionsPresenter - -protocol RegistrationPermissionsPresenter: AnyObject { - func requestPermissions() -> Guarantee -} - -// MARK: - RegistrationPermissionsViewController - -class RegistrationPermissionsViewController: OWSViewController { - private let state: RegistrationPermissionsState - private weak var presenter: RegistrationPermissionsPresenter? - - public init( - state: RegistrationPermissionsState, - presenter: RegistrationPermissionsPresenter? - ) { - self.state = state - self.presenter = presenter - - super.init() - } - - @available(*, unavailable) - public override init() { - owsFail("This should not be called") - } - - // MARK: Rendering - - private lazy var animationView: LottieAnimationView = { - let animationView = LottieAnimationView(name: "notificationPermission") - animationView.loopMode = .playOnce - animationView.backgroundBehavior = .pauseAndRestore - animationView.contentMode = .scaleAspectFit - return animationView - }() - - private var giveAccessButton: OWSFlatButton? - - public override func viewDidLoad() { - super.viewDidLoad() - - navigationItem.setHidesBackButton(true, animated: false) - - view.backgroundColor = Theme.backgroundColor - - let scrollView = UIScrollView() - view.addSubview(scrollView) - scrollView.autoPinEdgesToSuperviewEdges() - - let stackView = UIStackView() - stackView.axis = .vertical - stackView.alignment = .fill - stackView.layoutMargins = UIEdgeInsets.layoutMarginsForRegistration( - traitCollection.horizontalSizeClass - ) - stackView.isLayoutMarginsRelativeArrangement = true - stackView.setContentHuggingHigh() - scrollView.addSubview(stackView) - stackView.autoPinEdgesToSuperviewEdges() - stackView.autoMatch( - .width, - to: .width, - of: view, - withOffset: -view.layoutMargins.totalWidth, - relation: .equal - ) - let heightConstraint = stackView.heightAnchor.constraint( - greaterThanOrEqualTo: view.layoutMarginsGuide.heightAnchor - ) - heightConstraint.isActive = true - - let titleText: String - let explanationText: String - let giveAccessText: String - if state.shouldRequestAccessToContacts { - titleText = OWSLocalizedString( - "ONBOARDING_PERMISSIONS_TITLE", - comment: "Title of the 'onboarding permissions' view." - ) - explanationText = OWSLocalizedString( - "ONBOARDING_PERMISSIONS_EXPLANATION", - comment: "Explanation in the 'onboarding permissions' view." - ) - giveAccessText = OWSLocalizedString( - "ONBOARDING_PERMISSIONS_ENABLE_PERMISSIONS_BUTTON", - comment: "Label for the 'give access' button in the 'onboarding permissions' view." - ) - } else { - titleText = OWSLocalizedString( - "LINKED_ONBOARDING_PERMISSIONS_TITLE", - comment: "Title of the 'onboarding permissions' view." - ) - explanationText = OWSLocalizedString( - "LINKED_ONBOARDING_PERMISSIONS_EXPLANATION", - comment: "Explanation in the 'onboarding permissions' view." - ) - giveAccessText = OWSLocalizedString( - "LINKED_ONBOARDING_PERMISSIONS_ENABLE_PERMISSIONS_BUTTON", - comment: "Label for the 'give access' button in the 'onboarding permissions' view." - ) - } - - let titleLabel = UILabel.titleLabelForRegistration(text: titleText) - titleLabel.accessibilityIdentifier = "registration.permissions.titleLabel" - titleLabel.setCompressionResistanceVerticalHigh() - stackView.addArrangedSubview(titleLabel) - stackView.setCustomSpacing(20, after: titleLabel) - - let explanationLabel = UILabel.explanationLabelForRegistration(text: explanationText) - explanationLabel.accessibilityIdentifier = "registration.permissions.explanationLabel" - explanationLabel.setCompressionResistanceVerticalHigh() - stackView.addArrangedSubview(explanationLabel) - stackView.setCustomSpacing(60, after: explanationLabel) - - stackView.addArrangedSubview(animationView) - animationView.setContentHuggingHigh() - let animationSize = animationView.intrinsicContentSize - animationView.autoPin(toAspectRatio: animationSize.width / animationSize.height) - - stackView.addArrangedSubview(UIView.vStretchingSpacer(minHeight: 60)) - - let giveAccessButton = OWSFlatButton.primaryButtonForRegistration( - title: giveAccessText, - target: self, - selector: #selector(giveAccessPressed) - ) - giveAccessButton.accessibilityIdentifier = "registration.permissions.giveAccessButton" - stackView.addArrangedSubview(giveAccessButton) - giveAccessButton.autoHCenterInSuperview() - NSLayoutConstraint.autoSetPriority(.defaultLow) { - giveAccessButton.autoPinEdge(toSuperviewEdge: .leading) - giveAccessButton.autoPinEdge(toSuperviewEdge: .trailing) - } - NSLayoutConstraint.autoSetPriority(.required) { - giveAccessButton.autoSetDimension(.width, toSize: 280, relation: .greaterThanOrEqual) - giveAccessButton.autoSetDimension(.height, toSize: 50, relation: .greaterThanOrEqual) - } - self.giveAccessButton = giveAccessButton - } - - public override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - animationView.play() - } - - // MARK: Events - - @objc - private func giveAccessPressed() { - Logger.info("") - - requestPermissions() - } - - // MARK: Requesting permissions - - private func requestPermissions() { - giveAccessButton?.setEnabled(false) - presenter?.requestPermissions().observe(on: DispatchQueue.main) { [weak self] _ in - self?.giveAccessButton?.setEnabled(true) - } - } -} diff --git a/Signal/Registration/UserInterface/RegistrationPinAttemptsExhaustedAndMustCreateNewPinViewController.swift b/Signal/Registration/UserInterface/RegistrationPinAttemptsExhaustedAndMustCreateNewPinViewController.swift index 0e54187f18..50187265f1 100644 --- a/Signal/Registration/UserInterface/RegistrationPinAttemptsExhaustedAndMustCreateNewPinViewController.swift +++ b/Signal/Registration/UserInterface/RegistrationPinAttemptsExhaustedAndMustCreateNewPinViewController.swift @@ -120,9 +120,7 @@ class RegistrationPinAttemptsExhaustedAndMustCreateNewPinViewController: OWSView let stackView = UIStackView() stackView.axis = .vertical - stackView.layoutMargins = UIEdgeInsets.layoutMarginsForRegistration( - traitCollection.horizontalSizeClass - ) + stackView.directionalLayoutMargins = .layoutMarginsForRegistration(traitCollection.horizontalSizeClass) stackView.isLayoutMarginsRelativeArrangement = true view.addSubview(stackView) diff --git a/Signal/Registration/UserInterface/RegistrationReglockTimeoutViewController.swift b/Signal/Registration/UserInterface/RegistrationReglockTimeoutViewController.swift index f555c39788..71c7040c08 100644 --- a/Signal/Registration/UserInterface/RegistrationReglockTimeoutViewController.swift +++ b/Signal/Registration/UserInterface/RegistrationReglockTimeoutViewController.swift @@ -164,9 +164,7 @@ class RegistrationReglockTimeoutViewController: OWSViewController { let stackView = UIStackView() stackView.axis = .vertical - stackView.layoutMargins = UIEdgeInsets.layoutMarginsForRegistration( - traitCollection.horizontalSizeClass - ) + stackView.directionalLayoutMargins = .layoutMarginsForRegistration(traitCollection.horizontalSizeClass) stackView.isLayoutMarginsRelativeArrangement = true view.addSubview(stackView) diff --git a/Signal/Registration/UserInterface/RegistrationSplashViewController.swift b/Signal/Registration/UserInterface/RegistrationSplashViewController.swift index e3f869a54a..816598b214 100644 --- a/Signal/Registration/UserInterface/RegistrationSplashViewController.swift +++ b/Signal/Registration/UserInterface/RegistrationSplashViewController.swift @@ -38,9 +38,9 @@ public class RegistrationSplashViewController: OWSViewController { let stackView = UIStackView() stackView.axis = .vertical stackView.alignment = .fill - stackView.layoutMargins = { + stackView.directionalLayoutMargins = { let horizontalSizeClass = traitCollection.horizontalSizeClass - var result = UIEdgeInsets.layoutMarginsForRegistration(horizontalSizeClass) + var result = NSDirectionalEdgeInsets.layoutMarginsForRegistration(horizontalSizeClass) // We want the hero image a bit closer to the top. result.top = 16 return result diff --git a/Signal/Registration/UserInterface/RegistrationVerificationViewController.swift b/Signal/Registration/UserInterface/RegistrationVerificationViewController.swift index a7e58e9a81..36d5d3d571 100644 --- a/Signal/Registration/UserInterface/RegistrationVerificationViewController.swift +++ b/Signal/Registration/UserInterface/RegistrationVerificationViewController.swift @@ -261,9 +261,7 @@ class RegistrationVerificationViewController: OWSViewController { let stackView = UIStackView() stackView.axis = .vertical stackView.spacing = 12 - stackView.layoutMargins = UIEdgeInsets.layoutMarginsForRegistration( - traitCollection.horizontalSizeClass - ) + stackView.directionalLayoutMargins = .layoutMarginsForRegistration(traitCollection.horizontalSizeClass) stackView.isLayoutMarginsRelativeArrangement = true stackView.setContentHuggingHigh() scrollView.addSubview(stackView) diff --git a/Signal/Registration/UserInterface/RegistrationViewUtil.swift b/Signal/Registration/UserInterface/RegistrationViewUtil.swift index 1b252fa28b..8bd01eb94d 100644 --- a/Signal/Registration/UserInterface/RegistrationViewUtil.swift +++ b/Signal/Registration/UserInterface/RegistrationViewUtil.swift @@ -18,17 +18,17 @@ extension String { // MARK: - Layout margins -extension UIEdgeInsets { +extension NSDirectionalEdgeInsets { static func layoutMarginsForRegistration( _ horizontalSizeClass: UIUserInterfaceSizeClass - ) -> UIEdgeInsets { + ) -> NSDirectionalEdgeInsets { switch horizontalSizeClass { - case .unspecified, .compact: - return UIEdgeInsets(allButTop: 32) case .regular: - return UIEdgeInsets(allButTop: 112) + return NSDirectionalEdgeInsets(top: 0, leading: 112, bottom: 112, trailing: 112) + case .unspecified, .compact: + fallthrough @unknown default: - return UIEdgeInsets(allButTop: 32) + return NSDirectionalEdgeInsets(top: 0, leading: 32, bottom: 32, trailing: 32) } } diff --git a/Signal/test/Registration/RegistrationCoordinatorTest.swift b/Signal/test/Registration/RegistrationCoordinatorTest.swift index fba1e76c5c..c7c8d53208 100644 --- a/Signal/test/Registration/RegistrationCoordinatorTest.swift +++ b/Signal/test/Registration/RegistrationCoordinatorTest.swift @@ -231,15 +231,15 @@ public class RegistrationCoordinatorTest: XCTestCase { } // Now we should show the permissions. - XCTAssertEqual(nextStep.value, .permissions(Stubs.permissionsState())) + XCTAssertEqual(nextStep.value, .permissions) // Doesn't change even if we try and proceed. - XCTAssertEqual(coordinator.nextStep().value, .permissions(Stubs.permissionsState())) + XCTAssertEqual(coordinator.nextStep().value, .permissions) // Once the state is updated we can proceed. nextStep = coordinator.requestPermissions() XCTAssertNotNil(nextStep.value) XCTAssertNotEqual(nextStep.value, .registrationSplash) - XCTAssertNotEqual(nextStep.value, .permissions(Stubs.permissionsState())) + XCTAssertNotEqual(nextStep.value, .permissions) } } @@ -3693,7 +3693,7 @@ public class RegistrationCoordinatorTest: XCTestCase { // Now we should show the permissions. nextStep = coordinator.continueFromSplash() scheduler.runUntilIdle() - XCTAssertEqual(nextStep.value, .permissions(Stubs.permissionsState())) + XCTAssertEqual(nextStep.value, .permissions) // Once the state is updated we can proceed. nextStep = coordinator.requestPermissions() @@ -3907,10 +3907,6 @@ public class RegistrationCoordinatorTest: XCTestCase { // MARK: Step States - static func permissionsState() -> RegistrationPermissionsState { - return RegistrationPermissionsState(shouldRequestAccessToContacts: true) - } - static func pinEntryStateForRegRecoveryPath( mode: RegistrationMode, error: RegistrationPinValidationError? = nil, diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index ef4fe1fdef..12639a5f8c 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -4747,11 +4747,20 @@ /* warning to the user that linking a phone is not recommended */ "ONBOARDING_MODE_SWITCH_WARNING_REGISTERING" = "Linking your iPhone is not recommended and will limit core functionality."; -/* Label for the 'give access' button in the 'onboarding permissions' view. */ -"ONBOARDING_PERMISSIONS_ENABLE_PERMISSIONS_BUTTON" = "Allow Permissions"; +/* Description of the 'Contacts' permission in the 'onboarding permissions' view. */ +"ONBOARDING_PERMISSIONS_CONTACTS_DESCRIPTION" = "Find people you know. Your contacts are encrypted and not visible to the Signal service."; -/* Explanation in the 'onboarding permissions' view. */ -"ONBOARDING_PERMISSIONS_EXPLANATION" = "Allowing notifications and contacts lets you see when messages arrive and helps you find people you know. Contacts are encrypted so the Signal service can't see them."; +/* Title introducing the 'Contacts' permission in the 'onboarding permissions' view. */ +"ONBOARDING_PERMISSIONS_CONTACTS_TITLE" = "Contacts"; + +/* Description of the 'Notifications' permission in the 'onboarding permissions' view. */ +"ONBOARDING_PERMISSIONS_NOTIFICATIONS_DESCRIPTION" = "Get notified when new messages arrive."; + +/* Title introducing the 'Notifications' permission in the 'onboarding permissions' view. */ +"ONBOARDING_PERMISSIONS_NOTIFICATIONS_TITLE" = "Notifications"; + +/* Preamble of the 'onboarding permissions' view. */ +"ONBOARDING_PERMISSIONS_PREAMBLE" = "Signal would like to request the following permissions."; /* Title of the 'onboarding permissions' view. */ "ONBOARDING_PERMISSIONS_TITLE" = "Allow Permissions"; diff --git a/SignalUI/SwiftUIExtensions/AccessibleLayoutMetric.swift b/SignalUI/SwiftUIExtensions/AccessibleLayoutMetric.swift new file mode 100644 index 0000000000..e038a16bad --- /dev/null +++ b/SignalUI/SwiftUIExtensions/AccessibleLayoutMetric.swift @@ -0,0 +1,54 @@ +// +// Copyright 2024 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +// + +import SwiftUI + +/// A value that is automatically scaled down at accessibility dynamic type sizes. +/// +/// This is similar to SwiftUI's `ScaledMetric`, which is designed to scale values +/// *up*, proportionally with dynamic type size. `AccessibleLayoutMetric` is instead +/// designed to make more space for content by tightening up spacing metrics at +/// large dynamic type sizes. +/// +/// ```swift +/// struct ContentView: View { +/// // Automatically scales down to 67% at accessibility dynamic type sizes. +/// @AccessibleLayoutMetric private var rowSpacing = 24 +/// +/// // The scale used at accessibility sizes can be customized. +/// @AccessibleLayoutMetric(scale: 0.5) private var viewPadding = 24 +/// +/// var body: some View { +/// VStack(spacing: spacing) { +/// Text("Moderately long text") +/// Text("Very long text…") +/// } +/// .padding(.horizontal, viewPadding) +/// } +/// } +/// ``` +@propertyWrapper +public struct AccessibleLayoutMetric: DynamicProperty { + @Environment(\.dynamicTypeSize) private var dynamicTypeSize + private let accessibilityScale: Value + + public let rawValue: Value + public private(set) var wrappedValue: Value + + public init(wrappedValue: Value, scale: Value = 0.67) { + self.accessibilityScale = scale + self.rawValue = wrappedValue + self.wrappedValue = wrappedValue + } + + public var projectedValue: Self { + self + } + + public mutating func update() { + let scale = dynamicTypeSize.isAccessibilitySize ? accessibilityScale : 1.0 + wrappedValue = rawValue * scale + } +} diff --git a/SignalUI/SwiftUIExtensions/AsyncViewTask.swift b/SignalUI/SwiftUIExtensions/AsyncViewTask.swift new file mode 100644 index 0000000000..065be76e3e --- /dev/null +++ b/SignalUI/SwiftUIExtensions/AsyncViewTask.swift @@ -0,0 +1,79 @@ +// +// Copyright 2024 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +// + +import SwiftUI + +// MARK: - AsyncViewTask + +/// Represents an async operation that can be associated with the lifetime of +/// a view using the `task(_:)` view modifier. +public protocol AsyncViewTask: Identifiable { + /// Optionally provide a custom priority for the task. By default, the task + /// will be executed with the `.userInitied` priority. + /// See the `task(id:priority:_:)` view modifier for more information. + var priority: TaskPriority? { get } + + /// The asynchronous action performed by the task. + func perform() async +} + +extension AsyncViewTask { + public var priority: TaskPriority? { nil } +} + +// MARK: - AsyncViewTaskModifier + +extension View { + /// Associates a binding to an `AsyncViewTask` with the lifetime of a view. + /// + /// When the binding is `nil`, the task is not executing. To begin the task + /// set the value of the binding to an instance of the `AsyncTask` type. + /// + /// Buttons and other controls are automatically disabled while the task is + /// executing. + /// + /// To cancel the active task, set the value of the binding to `nil` or a + /// new task value. The previous task will automatically be cancelled before + /// a new task begins. + /// + /// ```swift + /// struct Nap: AsyncViewTask { + /// let id = UUID() + /// var duration: ContinousClock.Duration + /// + /// func perform() async { + /// try? await Task.sleep(for: duration) + /// } + /// } + /// + /// struct NappingButton: View { + /// @State private var nap: Nap? + /// + /// var body: some View { + /// Button("Take a Nap") { + /// nap = Nap(duration: .seconds(60)) + /// } + /// .task($nap.animation()) + /// } + /// } + /// ``` + public func task(_ task: Binding) -> some View { + modifier(AsyncViewTaskModifier(task: task)) + } +} + +public struct AsyncViewTaskModifier: ViewModifier { + @Binding var task: Task? + + public func body(content: Content) -> some View { + content + .disabled(task != nil) + .task(id: task?.id, priority: task?.priority ?? .userInitiated) { + guard let currentTask = task else { return } + defer { task = nil } + await currentTask.perform() + } + } +} diff --git a/SignalUI/SwiftUIExtensions/ScrollBounceBehaviorIfAvailable.swift b/SignalUI/SwiftUIExtensions/ScrollBounceBehaviorIfAvailable.swift new file mode 100644 index 0000000000..e1d5988b8b --- /dev/null +++ b/SignalUI/SwiftUIExtensions/ScrollBounceBehaviorIfAvailable.swift @@ -0,0 +1,39 @@ +// +// Copyright 2024 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +// + +import SwiftUI + +public struct ScrollBounceBehaviorIfAvailableModifier: ViewModifier { + public enum Behavior { + case automatic + case always + case basedOnSize + + @available(iOS 16.4, *) + var asScrollBounceBehavior: ScrollBounceBehavior { + switch self { + case .automatic: .automatic + case .always: .always + case .basedOnSize: .basedOnSize + } + } + } + + var behavior: Behavior + + public func body(content: Content) -> some View { + if #available(iOS 16.4, *) { + content.scrollBounceBehavior(behavior.asScrollBounceBehavior) + } else { + content + } + } +} + +extension View { + public func scrollBounceBehaviorIfAvailable(_ behavior: ScrollBounceBehaviorIfAvailableModifier.Behavior) -> some View { + modifier(ScrollBounceBehaviorIfAvailableModifier(behavior: behavior)) + } +}