From 049b68c4ea2c4cc8755fd65d78955e96ad109582 Mon Sep 17 00:00:00 2001 From: Michelle Linington Date: Mon, 21 Mar 2022 20:22:16 -0700 Subject: [PATCH 01/10] Better description for OWSHTTPHeaders --- SignalServiceKit/src/Network/OWSHttpHeaders.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SignalServiceKit/src/Network/OWSHttpHeaders.swift b/SignalServiceKit/src/Network/OWSHttpHeaders.swift index 9410b1b183..c720a5663f 100644 --- a/SignalServiceKit/src/Network/OWSHttpHeaders.swift +++ b/SignalServiceKit/src/Network/OWSHttpHeaders.swift @@ -186,6 +186,10 @@ public class OWSHttpHeaders: NSObject { request.replace(httpHeaders: httpHeaders) return request } + + // MARK: - NSObject Overrides + + override public var description: String { "<\(super.description): \(headers)>" } } // MARK: - HTTP Headers From fc69576a74c5752d598c070eea6fad612a59f796 Mon Sep 17 00:00:00 2001 From: Michelle Linington Date: Tue, 22 Mar 2022 23:15:09 -0700 Subject: [PATCH 02/10] Include status code in network request success log message --- .../src/Network/API/RESTNetworkManager.m | 2 +- .../src/Network/WebSockets/OWSWebSocket.swift | 39 +++++++++---------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/SignalServiceKit/src/Network/API/RESTNetworkManager.m b/SignalServiceKit/src/Network/API/RESTNetworkManager.m index c5dff3c89c..fed220aea3 100644 --- a/SignalServiceKit/src/Network/API/RESTNetworkManager.m +++ b/SignalServiceKit/src/Network/API/RESTNetworkManager.m @@ -178,7 +178,7 @@ NS_ASSUME_NONNULL_BEGIN }); dispatch_async(completionQueue, ^{ - OWSLogInfo(@"%@ succeeded : %@", label, request); + OWSLogInfo(@"%@ succeeded (%ld) : %@", label, response.responseStatusCode, request); if (request.canUseAuth && request.shouldHaveAuthorizationHeaders) { [RESTNetworkManager.tsAccountManager setIsDeregistered:NO]; diff --git a/SignalServiceKit/src/Network/WebSockets/OWSWebSocket.swift b/SignalServiceKit/src/Network/WebSockets/OWSWebSocket.swift index 6a304aec69..62761f1783 100644 --- a/SignalServiceKit/src/Network/WebSockets/OWSWebSocket.swift +++ b/SignalServiceKit/src/Network/WebSockets/OWSWebSocket.swift @@ -1154,31 +1154,28 @@ extension OWSWebSocket { let webSocketType = self.webSocketType let canUseAuth = webSocketType == .identified && !request.isUDRequest - self.makeRequestInternal(request, - unsubmittedRequestToken: unsubmittedRequestToken, - success: { (response: HTTPResponse, requestInfo: SocketRequestInfo) in - let label = Self.label(forRequest: request, - webSocketType: webSocketType, - requestInfo: requestInfo) + self.makeRequestInternal( + request, + unsubmittedRequestToken: unsubmittedRequestToken, + success: { (response: HTTPResponse, requestInfo: SocketRequestInfo) in + let label = Self.label(forRequest: request, webSocketType: webSocketType, requestInfo: requestInfo) + Logger.info("\(label): Request Succeeded (\(response.responseStatusCode))") - Logger.info("\(label): Request Succeeded") + if canUseAuth, request.shouldHaveAuthorizationHeaders { + Self.tsAccountManager.setIsDeregistered(false) + } - if canUseAuth, - request.shouldHaveAuthorizationHeaders { - Self.tsAccountManager.setIsDeregistered(false) - } + successParam(response) - successParam(response) + Self.outageDetection.reportConnectionSuccess() + }, + failure: { (failure: OWSHTTPErrorWrapper) in + if failure.error.responseStatusCode == AppExpiry.appExpiredStatusCode { + Self.appExpiry.setHasAppExpiredAtCurrentVersion() + } - Self.outageDetection.reportConnectionSuccess() - }, - failure: { (failure: OWSHTTPErrorWrapper) in - if failure.error.responseStatusCode == AppExpiry.appExpiredStatusCode { - Self.appExpiry.setHasAppExpiredAtCurrentVersion() - } - - failureParam(failure) - }) + failureParam(failure) + }) } } From ea25ebb9ff56fa6dd31ee0f240bcf19b6d92d437 Mon Sep 17 00:00:00 2001 From: Michelle Linington Date: Wed, 23 Mar 2022 12:29:00 -0700 Subject: [PATCH 03/10] Don't show USMI emoji in picker sheet --- .../EmojiPickerCollectionView.swift | 61 ++++++++++++++++++- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Emoji Picker/EmojiPickerCollectionView.swift b/Signal/src/ViewControllers/ConversationView/Emoji Picker/EmojiPickerCollectionView.swift index 0a15f3ccb3..058a213699 100644 --- a/Signal/src/ViewControllers/ConversationView/Emoji Picker/EmojiPickerCollectionView.swift +++ b/Signal/src/ViewControllers/ConversationView/Emoji Picker/EmojiPickerCollectionView.swift @@ -60,14 +60,32 @@ class EmojiPickerCollectionView: UICollectionView { layout.sectionInset = UIEdgeInsets(top: 0, leading: EmojiPickerCollectionView.margins, bottom: 0, trailing: EmojiPickerCollectionView.margins) (recentEmoji, allAvailableEmojiByCategory) = SDSDatabaseStorage.shared.read { transaction in - let rawEmoji = EmojiPickerCollectionView.keyValueStore.getObject( + let rawRecentEmoji = EmojiPickerCollectionView.keyValueStore.getObject( forKey: EmojiPickerCollectionView.recentEmojiKey, transaction: transaction ) as? [String] ?? [] - let recentEmoji = rawEmoji.compactMap { EmojiWithSkinTones(rawValue: $0) } + + var recentEmoji = rawRecentEmoji.compactMap { EmojiWithSkinTones(rawValue: $0) } + + // Some emoji have two different code points but identical appearances. Let's remove them! + // If we normalize to a different emoji than the one currently in our array, we want to drop + // the non-normalized variant if the normalized variant already exists. Otherwise, map to the + // normalized variant. + for (idx, emoji) in recentEmoji.enumerated().reversed() { + if !emoji.isNormalized { + if recentEmoji.contains(emoji.normalized) { + recentEmoji.remove(at: idx) + } else { + recentEmoji[idx] = emoji.normalized + } + } + } + let allAvailableEmojiByCategory = Emoji.allAvailableEmojiByCategoryWithPreferredSkinTones( transaction: transaction - ) + ).mapValues { categoryArray in + categoryArray.filter { $0.isNormalized } + } return (recentEmoji, allAvailableEmojiByCategory) } @@ -149,6 +167,10 @@ class EmojiPickerCollectionView: UICollectionView { func recordRecentEmoji(_ emoji: EmojiWithSkinTones, transaction: SDSAnyWriteTransaction) { guard recentEmoji.first != emoji else { return } + guard emoji.isNormalized else { + recordRecentEmoji(emoji.normalized, transaction: transaction) + return + } var newRecentEmoji = recentEmoji @@ -594,3 +616,36 @@ private class EmojiSearchIndex: NSObject { return index } } + +fileprivate extension Emoji { + + /// Both the US and USMI flags are identical in appearance. As far as I'm aware, these are the only two that are consistently identical + /// We only want to show one of these in the sheet, so we map the USMI flag to the US flag + /// If this ever expands in the future we might want to embed this in the EmojiGenerator script but I doubt that's likely. + var normalized: Emoji { + switch self { + case .flagUm: + return .us + default: + return self + } + } + + var isNormalized: Bool { self == normalized } + +} + +fileprivate extension EmojiWithSkinTones { + + var normalized: EmojiWithSkinTones { + switch (baseEmoji, skinTones) { + case (let base, nil) where base.normalized != base: + return EmojiWithSkinTones(baseEmoji: base.normalized) + default: + return self + } + } + + var isNormalized: Bool { self == normalized } + +} From 5d404bb35005f0c69e69ab742992e43d03853eaa Mon Sep 17 00:00:00 2001 From: Michelle Linington Date: Wed, 23 Mar 2022 12:41:02 -0700 Subject: [PATCH 04/10] IOS-2286: Onboarding loop during profile creation failure --- .../OnboardingProfileCreationViewController.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift b/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift index f65e366378..c20efa469f 100644 --- a/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift +++ b/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift @@ -345,8 +345,6 @@ public class OnboardingProfileCreationViewController: OnboardingBaseViewControll profileAvatarData: avatarData, visibleBadgeIds: [], userProfileWriter: .registration) - }.recover { error in - owsFailDebug("Error: \(error)") }.done { self.profileCompleted() UIView.animate(withDuration: 0.15) { @@ -354,6 +352,16 @@ public class OnboardingProfileCreationViewController: OnboardingBaseViewControll } completion: { _ in spinner.removeFromSuperview() } + }.recover { error in + if error.isNetworkConnectivityFailure { + OWSActionSheets.showErrorAlert( + message: NSLocalizedString( + "PROFILE_VIEW_NO_CONNECTION", + comment: "Error shown when the user tries to update their profile when the app is not connected to the internet.") + ) + } else { + OWSActionSheets.showErrorAlert(message: error.userErrorDescription) + } } } From 350cc16c17192d452eb47fd9cc06ea0eefd465ff Mon Sep 17 00:00:00 2001 From: Michelle Linington Date: Wed, 23 Mar 2022 12:43:28 -0700 Subject: [PATCH 05/10] Update fastlane dependency --- Gemfile.lock | 99 ++++++++++++++++++++++++++++------------------------ 1 file changed, 54 insertions(+), 45 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3ceacfcb3a..dab0b3454d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,7 +10,8 @@ GIT GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.3) + CFPropertyList (3.0.5) + rexml activesupport (5.2.6) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) @@ -24,21 +25,21 @@ GEM anbt-sql-formatter (0.1.0) artifactory (3.0.15) atomos (0.1.3) - aws-eventstream (1.1.1) - aws-partitions (1.479.0) - aws-sdk-core (3.117.0) + aws-eventstream (1.2.0) + aws-partitions (1.568.0) + aws-sdk-core (3.130.0) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) + aws-partitions (~> 1, >= 1.525.0) aws-sigv4 (~> 1.1) jmespath (~> 1.0) - aws-sdk-kms (1.44.0) - aws-sdk-core (~> 3, >= 3.112.0) + aws-sdk-kms (1.55.0) + aws-sdk-core (~> 3, >= 3.127.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.96.2) - aws-sdk-core (~> 3, >= 3.112.0) + aws-sdk-s3 (1.113.0) + aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.4) + aws-sigv4 (~> 1.4) + aws-sigv4 (1.4.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) claide (1.0.3) @@ -85,25 +86,27 @@ GEM highline (~> 2.0.0) concurrent-ruby (1.1.9) declarative (0.0.20) - digest-crc (0.6.3) + digest-crc (0.6.4) rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) dotenv (2.7.6) - emoji_regex (3.2.2) + emoji_regex (3.2.3) escape (0.0.4) ethon (0.14.0) ffi (>= 1.15.0) - excon (0.85.0) - faraday (1.5.1) + excon (0.92.1) + faraday (1.10.0) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.1) + faraday-net_http_persistent (~> 1.0) faraday-patron (~> 1.0) - multipart-post (>= 1.2, < 3) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) ruby2_keywords (>= 0.0.4) faraday-cookie_jar (0.0.7) faraday (>= 0.8.0) @@ -112,13 +115,17 @@ GEM faraday-em_synchrony (1.0.0) faraday-excon (1.1.0) faraday-httpclient (1.0.1) + faraday-multipart (1.0.3) + multipart-post (>= 1.2, < 3) faraday-net_http (1.0.1) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) - faraday_middleware (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) faraday (~> 1.0) - fastimage (2.2.4) - fastlane (2.188.0) + fastimage (2.2.6) + fastlane (2.205.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -135,7 +142,7 @@ GEM faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) gh_inspector (>= 1.1.2, < 2.0.0) - google-apis-androidpublisher_v3 (~> 0.1) + google-apis-androidpublisher_v3 (~> 0.3) google-apis-playcustomapp_v1 (~> 0.1) google-cloud-storage (~> 1.31) highline (~> 2.0) @@ -144,6 +151,7 @@ GEM mini_magick (>= 4.9.4, < 5.0.0) multipart-post (~> 2.0.0) naturally (~> 2.2) + optparse (~> 0.1.1) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) security (= 0.1.3) @@ -160,9 +168,9 @@ GEM fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.8.0) + google-apis-androidpublisher_v3 (0.16.0) google-apis-core (>= 0.4, < 2.a) - google-apis-core (0.4.1) + google-apis-core (0.4.2) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -171,45 +179,45 @@ GEM retriable (>= 2.0, < 4.a) rexml webrick - google-apis-iamcredentials_v1 (0.6.0) + google-apis-iamcredentials_v1 (0.10.0) google-apis-core (>= 0.4, < 2.a) - google-apis-playcustomapp_v1 (0.5.0) + google-apis-playcustomapp_v1 (0.7.0) google-apis-core (>= 0.4, < 2.a) - google-apis-storage_v1 (0.6.0) + google-apis-storage_v1 (0.11.0) google-apis-core (>= 0.4, < 2.a) google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) google-cloud-env (1.5.0) faraday (>= 0.17.3, < 2.0) - google-cloud-errors (1.1.0) - google-cloud-storage (1.34.1) - addressable (~> 2.5) + google-cloud-errors (1.2.0) + google-cloud-storage (1.36.1) + addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) google-apis-storage_v1 (~> 0.1) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (0.16.2) - faraday (>= 0.17.3, < 2.0) + googleauth (1.1.2) + faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.14) + signet (>= 0.16, < 2.a) highline (2.0.3) http-cookie (1.0.4) domain_name (~> 0.5) httpclient (2.8.3) i18n (1.8.10) concurrent-ruby (~> 1.0) - jmespath (1.4.0) - json (2.5.1) - jwt (2.2.3) + jmespath (1.6.1) + json (2.6.1) + jwt (2.3.0) memoist (0.16.2) mini_magick (4.11.0) - mini_mime (1.1.0) + mini_mime (1.1.2) minitest (5.14.4) molinillo (0.6.6) multi_json (1.15.0) @@ -218,7 +226,8 @@ GEM nap (1.1.0) naturally (2.2.1) netrc (0.11.0) - os (1.1.1) + optparse (0.1.1) + os (1.1.4) plist (3.6.0) public_suffix (4.0.6) rake (13.0.6) @@ -233,9 +242,9 @@ GEM ruby2_keywords (0.0.5) rubyzip (2.3.2) security (0.1.3) - signet (0.15.0) - addressable (~> 2.3) - faraday (>= 0.17.3, < 2.0) + signet (0.16.1) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.0) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) simctl (1.6.8) @@ -245,7 +254,7 @@ GEM terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) thread_safe (0.3.6) - trailblazer-option (0.1.1) + trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.1) tty-spinner (0.9.3) @@ -257,14 +266,14 @@ GEM uber (0.1.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.7) - unicode-display_width (1.7.0) + unf_ext (0.0.8.1) + unicode-display_width (1.8.0) webrick (1.7.0) word_wrap (1.0.0) xcode-install (2.7.0) claide (>= 0.9.1, < 1.1.0) fastlane (>= 2.1.0, < 3.0.0) - xcodeproj (1.20.0) + xcodeproj (1.21.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) From 29dc1eefcdb7073eea9458e1ce5d3d35b8526d39 Mon Sep 17 00:00:00 2001 From: Michelle Linington Date: Wed, 23 Mar 2022 12:53:11 -0700 Subject: [PATCH 06/10] Lint --- .../Emoji Picker/EmojiPickerCollectionView.swift | 2 +- .../Onboarding/OnboardingProfileCreationViewController.swift | 2 +- SignalServiceKit/src/Network/API/RESTNetworkManager.m | 4 ++-- SignalServiceKit/src/Network/OWSHttpHeaders.swift | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Emoji Picker/EmojiPickerCollectionView.swift b/Signal/src/ViewControllers/ConversationView/Emoji Picker/EmojiPickerCollectionView.swift index 058a213699..45f26b698c 100644 --- a/Signal/src/ViewControllers/ConversationView/Emoji Picker/EmojiPickerCollectionView.swift +++ b/Signal/src/ViewControllers/ConversationView/Emoji Picker/EmojiPickerCollectionView.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // import Foundation diff --git a/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift b/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift index c20efa469f..12454b1d51 100644 --- a/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift +++ b/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // import Foundation diff --git a/SignalServiceKit/src/Network/API/RESTNetworkManager.m b/SignalServiceKit/src/Network/API/RESTNetworkManager.m index fed220aea3..dfe67c33fe 100644 --- a/SignalServiceKit/src/Network/API/RESTNetworkManager.m +++ b/SignalServiceKit/src/Network/API/RESTNetworkManager.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import "RESTNetworkManager.h" @@ -176,7 +176,7 @@ NS_ASSUME_NONNULL_BEGIN dispatch_async(NetworkManagerQueue(), ^{ [sessionManagerPool returnToPool:sessionManager]; }); - + dispatch_async(completionQueue, ^{ OWSLogInfo(@"%@ succeeded (%ld) : %@", label, response.responseStatusCode, request); diff --git a/SignalServiceKit/src/Network/OWSHttpHeaders.swift b/SignalServiceKit/src/Network/OWSHttpHeaders.swift index c720a5663f..70f395c259 100644 --- a/SignalServiceKit/src/Network/OWSHttpHeaders.swift +++ b/SignalServiceKit/src/Network/OWSHttpHeaders.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // import Foundation From f2bb180d659cfdb96f0ff62f6f624871d9923108 Mon Sep 17 00:00:00 2001 From: Michelle Linington Date: Wed, 23 Mar 2022 16:51:15 -0700 Subject: [PATCH 07/10] Leverage spinner error UI --- ...oardingProfileCreationViewController.swift | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift b/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift index 12454b1d51..433500d93c 100644 --- a/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift +++ b/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift @@ -352,15 +352,19 @@ public class OnboardingProfileCreationViewController: OnboardingBaseViewControll } completion: { _ in spinner.removeFromSuperview() } - }.recover { error in - if error.isNetworkConnectivityFailure { - OWSActionSheets.showErrorAlert( - message: NSLocalizedString( - "PROFILE_VIEW_NO_CONNECTION", - comment: "Error shown when the user tries to update their profile when the app is not connected to the internet.") - ) - } else { - OWSActionSheets.showErrorAlert(message: error.userErrorDescription) + }.catch { error in + spinner.stopAnimating(success: false) { + spinner.removeFromSuperview() + + if error.isNetworkConnectivityFailure { + OWSActionSheets.showErrorAlert( + message: NSLocalizedString( + "PROFILE_VIEW_NO_CONNECTION", + comment: "Error shown when the user tries to update their profile when the app is not connected to the internet.") + ) + } else { + OWSActionSheets.showErrorAlert(message: error.userErrorDescription) + } } } } From 0f3a2b4c2e4e05ed35909938267fa4d7e7b77e7c Mon Sep 17 00:00:00 2001 From: Michelle Linington Date: Wed, 23 Mar 2022 17:09:05 -0700 Subject: [PATCH 08/10] Perform emoji mapping in EmojiGenerator --- Scripts/EmojiGenerator.swift | 43 +++++++++++++++---- .../EmojiPickerCollectionView.swift | 40 +++++------------ Signal/src/util/Emoji/Emoji+Category.swift | 11 ++++- .../src/util/Emoji/EmojiWithSkinTones.swift | 6 +-- 4 files changed, 57 insertions(+), 43 deletions(-) diff --git a/Scripts/EmojiGenerator.swift b/Scripts/EmojiGenerator.swift index bb5345f953..3098dbf9a6 100755 --- a/Scripts/EmojiGenerator.swift +++ b/Scripts/EmojiGenerator.swift @@ -177,6 +177,17 @@ struct EmojiModel { } } + var isNormalized: Bool { enumName == normalizedEnumName } + var normalizedEnumName: String { + switch enumName { + // flagUm (US Minor Outlying Islands) looks identical to the + // US flag. We don't present it as a sendable reaction option + // This matches the iOS keyboard behavior. + case "flagUm": return "us" + default: return enumName + } + } + static func parseCodePointString(_ pointString: String) -> [UnicodeScalar] { return pointString .components(separatedBy: "-") @@ -413,15 +424,17 @@ extension EmojiGenerator { fileHandle.writeLine("") // Emoji lookup per category - fileHandle.writeLine("var emoji: [Emoji] {") + fileHandle.writeLine("var normalizedEmoji: [Emoji] {") fileHandle.indent { fileHandle.writeLine("switch self {") - let emojiPerCategory: [RemoteModel.EmojiCategory: [EmojiModel.EmojiDefinition]] - emojiPerCategory = emojiModel.definitions.reduce(into: [:]) { result, emojiDef in - var categoryList = result[emojiDef.category] ?? [] - categoryList.append(emojiDef) - result[emojiDef.category] = categoryList + let normalizedEmojiPerCategory: [RemoteModel.EmojiCategory: [EmojiModel.EmojiDefinition]] + normalizedEmojiPerCategory = emojiModel.definitions.reduce(into: [:]) { result, emojiDef in + if emojiDef.isNormalized { + var categoryList = result[emojiDef.category] ?? [] + categoryList.append(emojiDef) + result[emojiDef.category] = categoryList + } } for category in outputCategories { @@ -430,9 +443,9 @@ extension EmojiGenerator { case .smileysAndPeople: // Merge smileys & people. It's important we initially bucket these separately, // because we want the emojis to be sorted smileys followed by people - return emojiPerCategory[.smileys]! + emojiPerCategory[.people]! + return normalizedEmojiPerCategory[.smileys]! + normalizedEmojiPerCategory[.people]! default: - return emojiPerCategory[category]! + return normalizedEmojiPerCategory[category]! } }() @@ -469,6 +482,20 @@ extension EmojiGenerator { fileHandle.writeLine("}") } fileHandle.writeLine("}") + fileHandle.writeLine("") + + // Normalized variant mapping + fileHandle.writeLine("var isNormalized: Bool { normalized == self }") + fileHandle.writeLine("var normalized: Emoji {") + fileHandle.indent { + fileHandle.writeLine("switch self {") + emojiModel.definitions.filter { !$0.isNormalized }.forEach { + fileHandle.writeLine("case .\($0.enumName): return .\($0.normalizedEnumName)") + } + fileHandle.writeLine("default: return self") + fileHandle.writeLine("}") + } + fileHandle.writeLine("}") } fileHandle.writeLine("}") } diff --git a/Signal/src/ViewControllers/ConversationView/Emoji Picker/EmojiPickerCollectionView.swift b/Signal/src/ViewControllers/ConversationView/Emoji Picker/EmojiPickerCollectionView.swift index 45f26b698c..6eb42695ba 100644 --- a/Signal/src/ViewControllers/ConversationView/Emoji Picker/EmojiPickerCollectionView.swift +++ b/Signal/src/ViewControllers/ConversationView/Emoji Picker/EmojiPickerCollectionView.swift @@ -24,9 +24,9 @@ class EmojiPickerCollectionView: UICollectionView { private let recentEmoji: [EmojiWithSkinTones] var hasRecentEmoji: Bool { !recentEmoji.isEmpty } - private let allAvailableEmojiByCategory: [Emoji.Category: [EmojiWithSkinTones]] - private lazy var allAvailableEmoji: [EmojiWithSkinTones] = { - return Array(allAvailableEmojiByCategory.values).flatMap({$0}) + private let allSendableEmojiByCategory: [Emoji.Category: [EmojiWithSkinTones]] + private lazy var allSendableEmoji: [EmojiWithSkinTones] = { + return Array(allSendableEmojiByCategory.values).flatMap({$0}) }() static let emojiWidth: CGFloat = 38 @@ -59,7 +59,7 @@ class EmojiPickerCollectionView: UICollectionView { layout.minimumInteritemSpacing = EmojiPickerCollectionView.minimumSpacing layout.sectionInset = UIEdgeInsets(top: 0, leading: EmojiPickerCollectionView.margins, bottom: 0, trailing: EmojiPickerCollectionView.margins) - (recentEmoji, allAvailableEmojiByCategory) = SDSDatabaseStorage.shared.read { transaction in + (recentEmoji, allSendableEmojiByCategory) = SDSDatabaseStorage.shared.read { transaction in let rawRecentEmoji = EmojiPickerCollectionView.keyValueStore.getObject( forKey: EmojiPickerCollectionView.recentEmojiKey, transaction: transaction @@ -81,12 +81,10 @@ class EmojiPickerCollectionView: UICollectionView { } } - let allAvailableEmojiByCategory = Emoji.allAvailableEmojiByCategoryWithPreferredSkinTones( + let allSendableEmojiByCategory = Emoji.allSendableEmojiByCategoryWithPreferredSkinTones( transaction: transaction - ).mapValues { categoryArray in - categoryArray.filter { $0.isNormalized } - } - return (recentEmoji, allAvailableEmojiByCategory) + ) + return (recentEmoji, allSendableEmojiByCategory) } super.init(frame: .zero, collectionViewLayout: layout) @@ -139,7 +137,7 @@ class EmojiPickerCollectionView: UICollectionView { return [] } - guard let categoryEmoji = allAvailableEmojiByCategory[category] else { + guard let categoryEmoji = allSendableEmojiByCategory[category] else { owsFailDebug("Unexpectedly missing emoji for category \(category)") return [] } @@ -214,7 +212,7 @@ class EmojiPickerCollectionView: UICollectionView { func searchWithText(_ searchText: String?) { if let searchText = searchText { if let emojiSearchIndex = emojiSearchIndex { - emojiSearchResults = allAvailableEmoji.filter { emoji in + emojiSearchResults = allSendableEmoji.filter { emoji in let rawEmoji = emoji.baseEmoji.rawValue if let terms = emojiSearchIndex[rawEmoji] { for term in terms { @@ -226,7 +224,7 @@ class EmojiPickerCollectionView: UICollectionView { return false } } else { // Search on raw emoji name if no index is available - emojiSearchResults = allAvailableEmoji.filter { emoji in + emojiSearchResults = allSendableEmoji.filter { emoji in return emoji.baseEmoji.name.range(of: searchText, options: [.caseInsensitive, .anchored]) != nil } } @@ -617,24 +615,6 @@ private class EmojiSearchIndex: NSObject { } } -fileprivate extension Emoji { - - /// Both the US and USMI flags are identical in appearance. As far as I'm aware, these are the only two that are consistently identical - /// We only want to show one of these in the sheet, so we map the USMI flag to the US flag - /// If this ever expands in the future we might want to embed this in the EmojiGenerator script but I doubt that's likely. - var normalized: Emoji { - switch self { - case .flagUm: - return .us - default: - return self - } - } - - var isNormalized: Bool { self == normalized } - -} - fileprivate extension EmojiWithSkinTones { var normalized: EmojiWithSkinTones { diff --git a/Signal/src/util/Emoji/Emoji+Category.swift b/Signal/src/util/Emoji/Emoji+Category.swift index 7e5d3d2f83..df6d5d3e78 100644 --- a/Signal/src/util/Emoji/Emoji+Category.swift +++ b/Signal/src/util/Emoji/Emoji+Category.swift @@ -36,7 +36,7 @@ extension Emoji { } } - var emoji: [Emoji] { + var normalizedEmoji: [Emoji] { switch self { case .smileysAndPeople: return [ @@ -1887,7 +1887,6 @@ extension Emoji { .flagTz, .flagUa, .flagUg, - .flagUm, .flagUn, .us, .flagUy, @@ -3769,4 +3768,12 @@ extension Emoji { default: fatalError("Unexpected case \(self)") } } + + var isNormalized: Bool { normalized == self } + var normalized: Emoji { + switch self { + case .flagUm: return .us + default: return self + } + } } diff --git a/Signal/src/util/Emoji/EmojiWithSkinTones.swift b/Signal/src/util/Emoji/EmojiWithSkinTones.swift index e3ff7bd801..d5b5c8bd0b 100644 --- a/Signal/src/util/Emoji/EmojiWithSkinTones.swift +++ b/Signal/src/util/Emoji/EmojiWithSkinTones.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // public struct EmojiWithSkinTones: Hashable { @@ -31,9 +31,9 @@ public struct EmojiWithSkinTones: Hashable { extension Emoji { private static let keyValueStore = SDSKeyValueStore(collection: "Emoji+PreferredSkinTonePermutation") - static func allAvailableEmojiByCategoryWithPreferredSkinTones(transaction: SDSAnyReadTransaction) -> [Category: [EmojiWithSkinTones]] { + static func allSendableEmojiByCategoryWithPreferredSkinTones(transaction: SDSAnyReadTransaction) -> [Category: [EmojiWithSkinTones]] { return Category.allCases.reduce(into: [Category: [EmojiWithSkinTones]]()) { result, category in - result[category] = category.emoji.filter { $0.available }.map { $0.withPreferredSkinTones(transaction: transaction) } + result[category] = category.normalizedEmoji.filter { $0.available }.map { $0.withPreferredSkinTones(transaction: transaction) } } } From cebd09e17c0d2f63b8d4430cb8a0d76b6b31591d Mon Sep 17 00:00:00 2001 From: Michelle Linington Date: Wed, 23 Mar 2022 18:18:28 -0700 Subject: [PATCH 09/10] Disable button while trying to create profile --- .../Onboarding/OnboardingProfileCreationViewController.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift b/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift index 433500d93c..03f2584e61 100644 --- a/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift +++ b/Signal/src/ViewControllers/Registration/Onboarding/OnboardingProfileCreationViewController.swift @@ -335,6 +335,7 @@ public class OnboardingProfileCreationViewController: OnboardingBaseViewControll primaryView.addSubview(spinner) spinner.autoCenterInSuperviewMargins() spinner.startAnimating() + saveButton.setEnabled(false) firstly(on: .sharedUserInitiated) { OWSProfileManager.updateLocalProfilePromise( @@ -351,10 +352,12 @@ public class OnboardingProfileCreationViewController: OnboardingBaseViewControll spinner.alpha = 0 } completion: { _ in spinner.removeFromSuperview() + self.saveButton.setEnabled(self.isValidProfile) } }.catch { error in spinner.stopAnimating(success: false) { spinner.removeFromSuperview() + self.saveButton.setEnabled(self.isValidProfile) if error.isNetworkConnectivityFailure { OWSActionSheets.showErrorAlert( From f4539ab83c4a6bade951161794716c6e22653c56 Mon Sep 17 00:00:00 2001 From: Michelle Linington Date: Fri, 1 Apr 2022 14:53:51 -0700 Subject: [PATCH 10/10] PR Feedback: Restrict logging --- .../src/Network/OWSHttpHeaders.swift | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/SignalServiceKit/src/Network/OWSHttpHeaders.swift b/SignalServiceKit/src/Network/OWSHttpHeaders.swift index 70f395c259..b15dfbe409 100644 --- a/SignalServiceKit/src/Network/OWSHttpHeaders.swift +++ b/SignalServiceKit/src/Network/OWSHttpHeaders.swift @@ -189,7 +189,27 @@ public class OWSHttpHeaders: NSObject { // MARK: - NSObject Overrides - override public var description: String { "<\(super.description): \(headers)>" } + static let whitelistedLoggedHeaderKeys = Set([ + "retry-after", + "x-signal-timestamp" + // Have a header key who's value you'd like to see logged? Add it here (lowercased)! + ]) + + override public var description: String { + let loggedPairsString: String = headers.lazy + .filter { Self.whitelistedLoggedHeaderKeys.contains($0.key.lowercased()) == true } + .map { "\($0.key): \($0.value.description)" } + .joined(separator: "; ") + + let leftoverKeysString: String = headers.keys.lazy + .filter { Self.whitelistedLoggedHeaderKeys.contains($0.lowercased()) == false } + .joined(separator: ", ") + + return "<\(super.description)" + .appending(loggedPairsString.count > 0 ? " (\(loggedPairsString))" : "") + .appending(leftoverKeysString.count > 0 ? " (\(leftoverKeysString))" : "") + .appending(">") + } } // MARK: - HTTP Headers