Merge branch 'master' into martin/IOS-2049

This commit is contained in:
Martin Böttcher 2021-12-08 11:44:46 +01:00 committed by GitHub
commit 9ccf7c7958
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 164 additions and 106 deletions

View File

@ -43,7 +43,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>34</string>
<string>37</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LOGS_EMAIL</key>
@ -141,7 +141,7 @@
<string>INStartCallIntent</string>
</array>
<key>OWSBundleVersion4</key>
<string>5.27.0.34</string>
<string>5.27.0.37</string>
<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
<true/>
<key>UIAppFonts</key>

View File

@ -40,6 +40,13 @@ class AppSettingsViewController: OWSTableViewController2 {
name: .localNumberDidChange,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(subscriptionStateDidChange),
name: SubscriptionManager.SubscriptionJobQueueDidFinishJobNotification,
object: nil
)
}
@objc
@ -61,6 +68,13 @@ class AppSettingsViewController: OWSTableViewController2 {
updateTableContents()
}
@objc
func subscriptionStateDidChange() {
AssertIsOnMainThread()
updateTableContents()
}
override func themeDidChange() {
super.themeDidChange()
updateTableContents()
@ -70,6 +84,10 @@ class AppSettingsViewController: OWSTableViewController2 {
return SubscriptionManager.hasCurrentSubscriptionCached()
}()
private lazy var hasExpiredSubscription: Bool = {
return SubscriptionManager.mostRecentlyExpiredBadgeIDWithSneakyTransaction() != nil
}()
func updateTableContents() {
let contents = OWSTableContents()
@ -253,15 +271,29 @@ class AppSettingsViewController: OWSTableViewController2 {
))
if RemoteConfig.donorBadgeAcquisition {
if DonationUtilities.isApplePayAvailable {
section4.add(.disclosureItem(
icon: .settingsDonate,
name: self.hasCurrentSubscription ? NSLocalizedString("SETTINGS_CURRENT_SUBSCRIPTION", comment: "Title for the 'Subscription' link in settings.") : NSLocalizedString("SETTINGS_SUBSCRIPTION", comment: "Title for the 'become a sustainer' link in settings."),
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "sustainer"),
actionBlock: { [weak self] in
let vc = SubscriptionViewController()
self?.navigationController?.pushViewController(vc, animated: true)
section4.add(.init(customCellBlock: { [weak self] in
guard let self = self else { return UITableViewCell() }
let cell = OWSTableItem.buildCellWithAccessoryLabel(
icon: .settingsDonate,
itemName: self.hasCurrentSubscription ? NSLocalizedString("SETTINGS_CURRENT_SUBSCRIPTION", comment: "Title for the 'Subscription' link in settings.") : NSLocalizedString("SETTINGS_SUBSCRIPTION", comment: "Title for the 'become a sustainer' link in settings."),
accessoryType: .disclosureIndicator,
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "sustainer"))
if self.hasExpiredSubscription {
let imageView = UIImageView(image: UIImage(named: "info-solid-24")?.withRenderingMode(.alwaysTemplate))
imageView.tintColor = Theme.accentBlueColor
cell.contentView.addSubview(imageView)
imageView.autoSetDimensions(to: CGSize(square: 24))
imageView.autoVCenterInSuperview()
imageView.autoPinEdge(.trailing, to: .trailing, of: cell.contentView, withOffset: -8)
}
))
return cell
}, actionBlock: { [weak self] in
let vc = SubscriptionViewController()
self?.navigationController?.pushViewController(vc, animated: true)
}))
section4.add(.disclosureItem(
icon: .settingsBoost,

View File

@ -274,7 +274,7 @@ class SubscriptionViewController: OWSTableViewController2 {
}
let expiredBadgeID = SubscriptionManager.mostRecentlyExpiredBadgeIDWithSneakyTransaction()
guard let expiredBadgeID = expiredBadgeID else {
guard let expiredBadgeID = expiredBadgeID, expiredBadgeID != "BOOST" else {
return
}
@ -1526,6 +1526,7 @@ private class SubscriptionReadMoreSheet: InteractiveSheetViewController {
extension SubscriptionViewController: BadgeExpirationSheetDelegate {
func badgeExpirationSheetActionButtonTapped(_ badgeExpirationSheet: BadgeExpirationSheet) {
SubscriptionManager.clearMostRecentlyExpiredBadgeIDWithSneakyTransaction()
requestApplePayDonation()
}

View File

@ -1013,7 +1013,7 @@ public extension DebugUIScreenshots {
profileBio: nil,
profileBioEmoji: nil,
profileAvatarData: avatarData,
visibleBadgeIds: nil,
visibleBadgeIds: [],
userProfileWriter: .debugging
).asVoid()
}.catch(on: .global()) { error in

View File

@ -725,6 +725,21 @@ extension HVTableDataSource: UITableViewDataSource {
extension HVTableDataSource {
public func updateVisibleCellContent(at indexPath: IndexPath, for tableView: UITableView) -> Bool {
AssertIsOnMainThread()
if let primKey = threadViewModel(forIndexPath: indexPath)?.threadRecord.uniqueId, (tableView.indexPathsForVisibleRows ?? []).contains(indexPath) {
for cell in tableView.visibleCells {
if let homeCell = cell as? HomeViewCell, let myKey = homeCell.thread?.uniqueId, myKey == primKey, let token = buildCellConfigurationAndContentTokenSync(forIndexPath: indexPath)?.contentToken {
homeCell.reset()
homeCell.configure(cellContentToken: token)
return true
}
}
}
return false
}
fileprivate struct HVCellConfigurationAndContentToken {
let configuration: HomeViewCell.Configuration
let contentToken: HVCellContentToken

View File

@ -157,7 +157,10 @@ extension HomeViewController {
tableView.insertRows(at: [newIndexPath], with: rowAnimation)
}
case .update(let oldIndexPath):
tableView.reloadRows(at: [oldIndexPath], with: .none)
let tds = tableView.dataSource as? HVTableDataSource
if tds == nil || !tds!.updateVisibleCellContent(at: oldIndexPath, for: tableView) {
tableView.reloadRows(at: [oldIndexPath], with: .none)
}
}
}

View File

@ -59,7 +59,7 @@ public extension HomeViewController {
}
let expiredBadgeID = SubscriptionManager.mostRecentlyExpiredBadgeIDWithSneakyTransaction()
guard let expiredBadgeID = expiredBadgeID, expiredBadgeID != "BOOST" else { // TODO Add boost support
guard let expiredBadgeID = expiredBadgeID else {
return
}
@ -71,35 +71,61 @@ public extension HomeViewController {
return
}
// Fetch current subscriptions, required to populate badge assets
firstly {
SubscriptionManager.getSubscriptions()
}.done(on: .global()) { (subscriptions: [SubscriptionLevel]) in
for level in subscriptions {
if level.badge.id == expiredBadgeID {
// Fetch assets
firstly {
self.profileManager.badgeStore.populateAssetsOnBadge(level.badge)
}.done(on: .main) {
if expiredBadgeID == "BOOST" {
firstly {
SubscriptionManager.getBoostBadge()
}.done(on: .global()) { boostBadge in
firstly {
self.profileManager.badgeStore.populateAssetsOnBadge(boostBadge)
}.done(on: .main) {
SDSDatabaseStorage.shared.write { transaction in
SubscriptionManager.setShowExpirySheetOnHomeScreenKey(show: false, transaction: transaction)
}
let badgeSheet = BadgeExpirationSheet(badge: level.badge)
badgeSheet.delegate = self
self.present(badgeSheet, animated: true)
}.catch { error in
owsFailDebug("Failed to fetch badge assets for expiry \(error)")
SDSDatabaseStorage.shared.write { transaction in
SubscriptionManager.setShowExpirySheetOnHomeScreenKey(show: false, transaction: transaction)
}
break
let badgeSheet = BadgeExpirationSheet(badge: boostBadge)
badgeSheet.delegate = self
self.present(badgeSheet, animated: true)
}.catch { error in
owsFailDebug("Failed to fetch boost badge assets for expiry \(error)")
}
}.catch { error in
owsFailDebug("Failed to fetch boost badge for expiry \(error)")
}
} else {
// Fetch current subscriptions, required to populate badge assets
firstly {
SubscriptionManager.getSubscriptions()
}.done(on: .global()) { (subscriptions: [SubscriptionLevel]) in
let subscriptionLevel = subscriptions.first { $0.badge.id == expiredBadgeID }
guard let subscriptionLevel = subscriptionLevel else {
owsFailDebug("Unable to find matching subscription level for expired badge")
return
}
firstly {
self.profileManager.badgeStore.populateAssetsOnBadge(subscriptionLevel.badge)
}.done(on: .main) {
SDSDatabaseStorage.shared.write { transaction in
SubscriptionManager.setShowExpirySheetOnHomeScreenKey(show: false, transaction: transaction)
}
let badgeSheet = BadgeExpirationSheet(badge: subscriptionLevel.badge)
badgeSheet.delegate = self
self.present(badgeSheet, animated: true)
}.catch { error in
owsFailDebug("Failed to fetch subscription badge assets for expiry \(error)")
}
}.catch { error in
owsFailDebug("Failed to fetch subscriptions for expiry \(error)")
}
}.catch { error in
owsFailDebug("Failed to fetch subscriptions for expiry \(error)")
}
}
// MARK: -
@ -369,8 +395,12 @@ public extension HomeViewController {
extension HomeViewController: BadgeExpirationSheetDelegate {
func badgeExpirationSheetActionButtonTapped(_ badgeExpirationSheet: BadgeExpirationSheet) {
SubscriptionManager.clearMostRecentlyExpiredBadgeIDWithSneakyTransaction()
let mode: ShowAppSettingsMode = badgeExpirationSheet.badgeID == "BOOST" ? .boost : .subscriptions
showAppSettings(mode: mode)
switch (badgeID: badgeExpirationSheet.badgeID, isSustainer: SubscriptionManager.hasCurrentSubscriptionCached()) {
case (badgeID: "BOOST", isSustainer: true):
showAppSettings(mode: .boost)
default:
showAppSettings(mode: .subscriptions)
}
}
func badgeExpirationSheetNotNowButtonTapped(_ badgeExpirationSheet: BadgeExpirationSheet) { }

View File

@ -343,7 +343,7 @@ public class OnboardingProfileCreationViewController: OnboardingBaseViewControll
profileBio: nil,
profileBioEmoji: nil,
profileAvatarData: avatarData,
visibleBadgeIds: nil,
visibleBadgeIds: [],
userProfileWriter: .registration)
}.recover { error in
owsFailDebug("Error: \(error)")

View File

@ -87,23 +87,24 @@ public class ContactsMaps: NSObject {
}
fileprivate static func phoneNumbers(forContact contact: Contact, localNumber: String?) -> [String] {
return contact.parsedPhoneNumbers.compactMap { phoneNumber in
let phoneNumbers: [String] = contact.parsedPhoneNumbers.compactMap { phoneNumber in
guard let phoneNumberE164 = phoneNumber.toE164().nilIfEmpty else {
return nil
}
// Ignore any system contact records for the local contact.
// For the local user we never want to show the avatar /
// name that you have entered for yourself in your system
// contacts. Instead, we always want to display your profile
// name and avatar.
let isLocalContact = phoneNumberE164 == localNumber
guard !isLocalContact else {
return nil
}
return phoneNumberE164
}
if let localNumber = localNumber, phoneNumbers.contains(localNumber) {
// Ignore any system contact records for the local contact.
// For the local user we never want to show the avatar /
// name that you have entered for yourself in your system
// contacts. Instead, we always want to display your profile
// name and avatar.
return []
}
return phoneNumbers
}
@objc

View File

@ -566,6 +566,11 @@ extension OWSContactsManager {
return nil
}
guard !address.isLocalAddress else {
// Never use system contact or synced image data for the local user
return nil
}
if let phoneNumber = self.phoneNumber(for: address, transaction: transaction),
let contact = contactsManagerCache.contact(forPhoneNumber: phoneNumber, transaction: transaction),
let cnContactId = contact.cnContactId,

View File

@ -20,7 +20,7 @@ public extension OWSProfileManager {
profileBio: String?,
profileBioEmoji: String?,
profileAvatarData: Data?,
visibleBadgeIds: [String]?,
visibleBadgeIds: [String],
unsavedRotatedProfileKey: OWSAES256Key? = nil,
userProfileWriter: UserProfileWriter) -> Promise<Void> {
assert(CurrentAppContext().isMainApp)
@ -60,7 +60,7 @@ public extension OWSProfileManager {
let profileBio: String?
let profileBioEmoji: String?
let profileAvatarData: Data?
let visibleBadgeIds: [String]?
let visibleBadgeIds: [String]
let userProfileWriter: UserProfileWriter
if let pendingUpdate = (Self.databaseStorage.read { transaction in
return Self.currentPendingProfileUpdate(transaction: transaction)
@ -227,7 +227,7 @@ public extension OWSProfileManager {
profileBio: String?,
profileBioEmoji: String?,
profileAvatarData: Data?,
visibleBadgeIds: [String]?,
visibleBadgeIds: [String],
userProfileWriter: UserProfileWriter) -> AnyPromise {
return AnyPromise(updateLocalProfilePromise(
profileGivenName: profileGivenName,
@ -453,7 +453,7 @@ extension OWSProfileManager {
"profileFamilyName?: \(attempt.update.profileFamilyName != nil), " +
"profileBio?: \(attempt.update.profileBio != nil), " +
"profileBioEmoji?: \(attempt.update.profileBioEmoji != nil) " +
"visibleBadges?: \(attempt.update.visibleBadgeIds != nil).")
"visibleBadges?: \(attempt.update.visibleBadgeIds.count).")
return firstly(on: .global()) {
Self.versionedProfilesImpl.updateProfilePromise(profileGivenName: attempt.update.profileGivenName,
profileFamilyName: attempt.update.profileFamilyName,
@ -490,7 +490,7 @@ extension OWSProfileManager {
profileBio: String?,
profileBioEmoji: String?,
profileAvatarData: Data?,
visibleBadgeIds: [String]?,
visibleBadgeIds: [String],
unsavedRotatedProfileKey: OWSAES256Key?,
userProfileWriter: UserProfileWriter) -> PendingProfileUpdate {
Logger.verbose("")
@ -560,7 +560,7 @@ class PendingProfileUpdate: NSObject, NSCoding {
// If nil, we are clearing the profile avatar.
let profileAvatarData: Data?
let visibleBadgeIds: [String]?
let visibleBadgeIds: [String]
let unsavedRotatedProfileKey: OWSAES256Key?
@ -585,7 +585,7 @@ class PendingProfileUpdate: NSObject, NSCoding {
profileBio: String?,
profileBioEmoji: String?,
profileAvatarData: Data?,
visibleBadgeIds: [String]?,
visibleBadgeIds: [String],
unsavedRotatedProfileKey: OWSAES256Key?,
userProfileWriter: UserProfileWriter) {
@ -632,7 +632,7 @@ class PendingProfileUpdate: NSObject, NSCoding {
self.profileBio = aDecoder.decodeObject(forKey: "profileBio") as? String
self.profileBioEmoji = aDecoder.decodeObject(forKey: "profileBioEmoji") as? String
self.profileAvatarData = aDecoder.decodeObject(forKey: "profileAvatarData") as? Data
self.visibleBadgeIds = aDecoder.decodeObject(forKey: "visiboleBadgeIds") as? [String]
self.visibleBadgeIds = (aDecoder.decodeObject(forKey: "visibleBadgeIds") as? [String]) ?? []
self.unsavedRotatedProfileKey = aDecoder.decodeObject(forKey: "unsavedRotatedProfileKey") as? OWSAES256Key
if aDecoder.containsValue(forKey: "userProfileWriter"),
let userProfileWriter = UserProfileWriter(rawValue: UInt(aDecoder.decodeInt32(forKey: "userProfileWriter"))) {

View File

@ -41,7 +41,7 @@ public class VersionedProfilesImpl: NSObject, VersionedProfilesSwift {
profileBio: String?,
profileBioEmoji: String?,
profileAvatarData: Data?,
visibleBadgeIds: [String]?,
visibleBadgeIds: [String],
unsavedRotatedProfileKey: OWSAES256Key?) -> Promise<VersionedProfileUpdate> {
firstly(on: .global()) {

View File

@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>5.27.0</string>
<key>CFBundleVersion</key>
<string>34</string>
<string>37</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
@ -55,7 +55,7 @@
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
</dict>
<key>OWSBundleVersion4</key>
<string>5.27.0.34</string>
<string>5.27.0.37</string>
<key>UIAppFonts</key>
<array>
<string>Inter-Medium.otf</string>

View File

@ -179,7 +179,7 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
bioEmoji:(nullable ProfileValue *)bioEmoji
hasAvatar:(BOOL)hasAvatar
paymentAddress:(nullable ProfileValue *)paymentAddress
visibleBadgeIds:(nullable NSArray<NSString *> *)visibleBadgeIds
visibleBadgeIds:(NSArray<NSString *> *)visibleBadgeIds
version:(NSString *)version
commitment:(NSData *)commitment;

View File

@ -797,7 +797,7 @@ NSString *const OWSRequestKey_AuthKey = @"AuthKey";
bioEmoji:(nullable ProfileValue *)bioEmoji
hasAvatar:(BOOL)hasAvatar
paymentAddress:(nullable ProfileValue *)paymentAddress
visibleBadgeIds:(nullable NSArray<NSString *> *)visibleBadgeIds
visibleBadgeIds:(NSArray<NSString *> *)visibleBadgeIds
version:(NSString *)version
commitment:(NSData *)commitment
{
@ -828,9 +828,7 @@ NSString *const OWSRequestKey_AuthKey = @"AuthKey";
OWSAssertDebug(paymentAddress.hasValidBase64Length);
parameters[@"paymentAddress"] = paymentAddress.encryptedBase64;
}
if (visibleBadgeIds != nil) {
parameters[@"badgeIds"] = visibleBadgeIds;
}
parameters[@"badgeIds"] = [visibleBadgeIds copy];
NSURL *url = [NSURL URLWithString:self.textSecureVersionedProfileAPI];
return [TSRequest requestWithUrl:url

View File

@ -165,21 +165,9 @@ public class TSConstants: NSObject {
private class TSConstantsProduction: TSConstantsProtocol {
public var mainServiceWebSocketAPI_identified: String {
FeatureFlags.newHostNames
? "wss://chat.signal.org/v1/websocket/"
: "wss://textsecure-service.whispersystems.org/v1/websocket/"
}
public var mainServiceWebSocketAPI_unidentified: String {
FeatureFlags.newHostNames
? "wss://ud-chat.signal.org/v1/websocket/"
: "wss://textsecure-service.whispersystems.org/v1/websocket/"
}
public var mainServiceURL: String {
FeatureFlags.newHostNames
? "https://chat.signal.org/"
: "https://textsecure-service.whispersystems.org/"
}
public let mainServiceWebSocketAPI_identified = "wss://chat.signal.org/v1/websocket/"
public let mainServiceWebSocketAPI_unidentified = "wss://ud-chat.signal.org/v1/websocket/"
public let mainServiceURL = "https://chat.signal.org/"
public let textSecureCDN0ServerURL = "https://cdn.signal.org"
public let textSecureCDN2ServerURL = "https://cdn2.signal.org"
public let contactDiscoveryURL = "https://api.directory.signal.org"
@ -227,21 +215,9 @@ private class TSConstantsProduction: TSConstantsProtocol {
private class TSConstantsStaging: TSConstantsProtocol {
public var mainServiceWebSocketAPI_identified: String {
FeatureFlags.newHostNames
? "wss://chat.staging.signal.org/v1/websocket/"
: "wss://textsecure-service-staging.whispersystems.org/v1/websocket/"
}
public var mainServiceWebSocketAPI_unidentified: String {
FeatureFlags.newHostNames
? "wss://ud-chat.staging.signal.org/v1/websocket/"
: "wss://textsecure-service-staging.whispersystems.org/v1/websocket/"
}
public var mainServiceURL: String {
FeatureFlags.newHostNames
? "https://chat.staging.signal.org/"
: "https://textsecure-service-staging.whispersystems.org/"
}
public let mainServiceWebSocketAPI_identified = "wss://chat.staging.signal.org/v1/websocket/"
public let mainServiceWebSocketAPI_unidentified = "wss://ud-chat.staging.signal.org/v1/websocket/"
public let mainServiceURL = "https://chat.staging.signal.org/"
public let textSecureCDN0ServerURL = "https://cdn-staging.signal.org"
public let textSecureCDN2ServerURL = "https://cdn2-staging.signal.org"
public let contactDiscoveryURL = "https://api-staging.directory.signal.org"

View File

@ -224,9 +224,6 @@ public class FeatureFlags: BaseFlags {
@objc
public static let deprecateREST = false
@objc
public static let newHostNames = true
@objc
public static let groupRings = false

View File

@ -49,7 +49,7 @@ public protocol VersionedProfilesSwift: VersionedProfiles {
profileBio: String?,
profileBioEmoji: String?,
profileAvatarData: Data?,
visibleBadgeIds: [String]?,
visibleBadgeIds: [String],
unsavedRotatedProfileKey: OWSAES256Key?) -> Promise<VersionedProfileUpdate>
}
@ -75,7 +75,7 @@ public class MockVersionedProfiles: NSObject, VersionedProfilesSwift {
profileBio: String?,
profileBioEmoji: String?,
profileAvatarData: Data?,
visibleBadgeIds: [String]?,
visibleBadgeIds: [String],
unsavedRotatedProfileKey: OWSAES256Key?) -> Promise<VersionedProfileUpdate> {
owsFail("Not implemented.")
}

View File

@ -22,7 +22,7 @@ public class RemoteConfig: BaseFlags {
self.isEnabledFlags = isEnabledFlags
self.valueFlags = valueFlags
self.researchMegaphone = Self.isCountryCodeBucketEnabled(.researchMegaphone, valueFlags: valueFlags)
self.subscriptionMegaphone = Self.isCountryCodeBucketEnabled(.subscriptionMegaphone, valueFlags: valueFlags)
self.subscriptionMegaphone = Self.isCountryCodeBucketEnabled(.subscriptionMegaphone2, valueFlags: valueFlags)
self.standardMediaQualityLevel = Self.determineStandardMediaQualityLevel(valueFlags: valueFlags)
self.paymentsDisabledRegions = Self.parsePaymentsDisabledRegions(valueFlags: valueFlags)
}
@ -471,7 +471,7 @@ private struct Flags {
case replaceableInteractionExpiration
case messageSendLogEntryLifetime
case paymentsDisabledRegions
case subscriptionMegaphone
case subscriptionMegaphone2
case subscriptionMegaphoneSnoozeInterval
}
}

View File

@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>5.27.0</string>
<key>CFBundleVersion</key>
<string>34</string>
<string>37</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>NSAppTransportSecurity</key>
@ -77,7 +77,7 @@
<string>com.apple.share-services</string>
</dict>
<key>OWSBundleVersion4</key>
<string>5.27.0.34</string>
<string>5.27.0.37</string>
<key>UIAppFonts</key>
<array>
<string>fontawesome-webfont.ttf</string>