Merge branch 'mlin/PR/VariousFixes'

This commit is contained in:
Michelle Linington 2022-04-01 14:55:50 -07:00
commit 532db84e73
9 changed files with 212 additions and 98 deletions

View File

@ -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)

View File

@ -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("}")
}

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
//
import Foundation
@ -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,16 +59,32 @@ 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
let rawEmoji = EmojiPickerCollectionView.keyValueStore.getObject(
(recentEmoji, allSendableEmojiByCategory) = SDSDatabaseStorage.shared.read { transaction in
let rawRecentEmoji = EmojiPickerCollectionView.keyValueStore.getObject(
forKey: EmojiPickerCollectionView.recentEmojiKey,
transaction: transaction
) as? [String] ?? []
let recentEmoji = rawEmoji.compactMap { EmojiWithSkinTones(rawValue: $0) }
let allAvailableEmojiByCategory = Emoji.allAvailableEmojiByCategoryWithPreferredSkinTones(
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 allSendableEmojiByCategory = Emoji.allSendableEmojiByCategoryWithPreferredSkinTones(
transaction: transaction
)
return (recentEmoji, allAvailableEmojiByCategory)
return (recentEmoji, allSendableEmojiByCategory)
}
super.init(frame: .zero, collectionViewLayout: layout)
@ -121,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 []
}
@ -149,6 +165,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
@ -192,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 {
@ -204,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
}
}
@ -594,3 +614,18 @@ private class EmojiSearchIndex: NSObject {
return index
}
}
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 }
}

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
//
import Foundation
@ -335,6 +335,7 @@ public class OnboardingProfileCreationViewController: OnboardingBaseViewControll
primaryView.addSubview(spinner)
spinner.autoCenterInSuperviewMargins()
spinner.startAnimating()
saveButton.setEnabled(false)
firstly(on: .sharedUserInitiated) {
OWSProfileManager.updateLocalProfilePromise(
@ -345,14 +346,28 @@ public class OnboardingProfileCreationViewController: OnboardingBaseViewControll
profileAvatarData: avatarData,
visibleBadgeIds: [],
userProfileWriter: .registration)
}.recover { error in
owsFailDebug("Error: \(error)")
}.done {
self.profileCompleted()
UIView.animate(withDuration: 0.15) {
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(
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)
}
}
}
}

View File

@ -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
}
}
}

View File

@ -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) }
}
}

View File

@ -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,9 +176,9 @@ NS_ASSUME_NONNULL_BEGIN
dispatch_async(NetworkManagerQueue(), ^{
[sessionManagerPool returnToPool:sessionManager];
});
dispatch_async(completionQueue, ^{
OWSLogInfo(@"%@ succeeded : %@", label, request);
OWSLogInfo(@"%@ succeeded (%ld) : %@", label, response.responseStatusCode, request);
if (request.canUseAuth && request.shouldHaveAuthorizationHeaders) {
[RESTNetworkManager.tsAccountManager setIsDeregistered:NO];

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
//
import Foundation
@ -186,6 +186,30 @@ public class OWSHttpHeaders: NSObject {
request.replace(httpHeaders: httpHeaders)
return request
}
// MARK: - NSObject Overrides
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

View File

@ -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)
})
}
}