Sync additional badge info with storage service

This commit is contained in:
Nora Trapp 2021-12-17 14:07:40 -08:00
parent 8da582715f
commit f2c09045ca
14 changed files with 229 additions and 46 deletions

View File

@ -63,14 +63,7 @@ class BadgeConfigurationViewController: OWSTableViewController2, BadgeCollection
convenience init(fetchingDataFromLocalProfileWithDelegate delegate: BadgeConfigurationDelegate) {
let snapshot = Self.profileManagerImpl.localProfileSnapshot(shouldIncludeAvatar: false)
let allBadges = snapshot.profileBadgeInfo ?? []
let shouldDisplayOnProfile = allBadges.allSatisfy { badge in
if let isVisible = badge.isVisible {
return isVisible
} else {
owsFailDebug("Expecting visibility for local profile badge settings")
return true
}
}
let shouldDisplayOnProfile = Self.subscriptionManager.displayBadgesOnProfile
self.init(availableBadges: allBadges, shouldDisplayOnProfile: shouldDisplayOnProfile, delegate: delegate)
}

View File

@ -40,12 +40,7 @@ class ProfileSettingsViewController: OWSTableViewController2 {
bio = snapshot.bio
bioEmoji = snapshot.bioEmoji
allBadges = snapshot.profileBadgeInfo ?? []
displayBadgesOnProfile = allBadges.allSatisfy { badge in
badge.isVisible ?? {
owsFailDebug("Local user badges should always have a non-nil visibility flag")
return true
}()
}
displayBadgesOnProfile = subscriptionManager.displayBadgesOnProfile
updateTableContents()
}
@ -208,6 +203,7 @@ class ProfileSettingsViewController: OWSTableViewController2 {
let normalizedBio = self.normalizedBio
let normalizedBioEmoji = self.normalizedBioEmoji
let visibleBadgeIds = displayBadgesOnProfile ? self.allBadges.map { $0.badgeId } : []
let displayBadgesOnProfile = displayBadgesOnProfile
if !self.reachabilityManager.isReachable {
OWSActionSheets.showErrorAlert(message: NSLocalizedString("PROFILE_VIEW_NO_CONNECTION",
@ -227,6 +223,14 @@ class ProfileSettingsViewController: OWSTableViewController2 {
profileAvatarData: avatarData,
visibleBadgeIds: visibleBadgeIds,
userProfileWriter: .localUser)
}.then(on: .global()) { () -> Promise<Void> in
Self.databaseStorage.writePromise { transaction in
Self.subscriptionManager.setDisplayBadgesOnProfile(
displayBadgesOnProfile,
updateStorageService: true,
transaction: transaction
)
}.asVoid()
}.done(on: .main) { _ in
modalActivityIndicator.dismiss { [weak self] in
AssertIsOnMainThread()

View File

@ -1373,6 +1373,22 @@ extension SubscriptionViewController: BadgeConfigurationDelegate {
} else {
return Promise.value(())
}
}.then(on: .global()) { () -> Promise<Void> in
let displayBadgesOnProfile: Bool
switch setting {
case .doNotDisplayPublicly:
displayBadgesOnProfile = false
case .display:
displayBadgesOnProfile = true
}
return Self.databaseStorage.writePromise { transaction in
Self.subscriptionManager.setDisplayBadgesOnProfile(
displayBadgesOnProfile,
updateStorageService: true,
transaction: transaction
)
}.asVoid()
}.done {
self.navigationController?.popViewController(animated: true)
}.catch { error in

View File

@ -587,6 +587,9 @@ extension StorageServiceProtoAccountRecord: Dependencies {
builder.setSubscriberCurrencyCode(subscriberCurrencyCode)
}
builder.setDisplayBadgesOnProfile(subscriptionManager.displayBadgesOnProfile(transaction: transaction))
builder.setSubscriptionManuallyCancelled(subscriptionManager.userManuallyCancelledSubscription(transaction: transaction))
return try builder.build()
}
@ -756,6 +759,24 @@ extension StorageServiceProtoAccountRecord: Dependencies {
}
}
let localDisplayBadgesOnProfile = subscriptionManager.displayBadgesOnProfile(transaction: transaction)
if localDisplayBadgesOnProfile != displayBadgesOnProfile {
subscriptionManager.setDisplayBadgesOnProfile(
displayBadgesOnProfile,
updateStorageService: false,
transaction: transaction
)
}
let localSubscriptionManuallyCancelled = subscriptionManager.userManuallyCancelledSubscription(transaction: transaction)
if localSubscriptionManuallyCancelled != subscriptionManuallyCancelled {
subscriptionManager.setUserManuallyCancelledSubscription(
subscriptionManuallyCancelled,
updateStorageService: false,
transaction: transaction
)
}
return mergeState
}
}

View File

@ -5,6 +5,7 @@
import Foundation
import PassKit
import ZKGroup
import SignalServiceKit
public enum SubscriptionBadgeIds: String, CaseIterable {
case low = "R_LOW"
@ -111,10 +112,44 @@ public class SubscriptionManager: NSObject {
SwiftSingletons.register(self)
AppReadiness.runNowOrWhenAppDidBecomeReadyAsync {
Self.performSubscriptionKeepAliveIfNecessary()
DispatchQueue.global().async {
Self.warmCaches()
Self.performMigrationToStorageServiceIfNecessary()
Self.performSubscriptionKeepAliveIfNecessary()
}
}
}
private static func warmCaches() {
let value = databaseStorage.read { displayBadgesOnProfile(transaction: $0) }
displayBadgesOnProfileCache.set(value)
}
private static func performMigrationToStorageServiceIfNecessary() {
let hasMigratedToStorageService = databaseStorage.read { transaction in
subscriptionKVS.getBool(hasMigratedToStorageServiceKey, defaultValue: false, transaction: transaction)
}
guard !hasMigratedToStorageService else { return }
databaseStorage.write { transaction in
subscriptionKVS.setBool(true, key: hasMigratedToStorageServiceKey, transaction: transaction)
let localProfile = profileManagerImpl.localUserProfile()
let allBadges = localProfile.profileBadgeInfo ?? []
let displayBadgesOnProfile = allBadges.allSatisfy { badge in
badge.isVisible ?? {
owsFailDebug("Local user badges should always have a non-nil visibility flag")
return true
}()
}
setDisplayBadgesOnProfile(displayBadgesOnProfile, transaction: transaction)
}
storageServiceManager.recordPendingLocalAccountUpdates()
}
public static let subscriptionJobQueue = SubscriptionReceiptCredentialJobQueue()
public static let SubscriptionJobQueueDidFinishJobNotification = NSNotification.Name("SubscriptionJobQueueDidFinishJobNotification")
public static let SubscriptionJobQueueDidFailJobNotification = NSNotification.Name("SubscriptionJobQueueDidFailJobNotification")
@ -126,10 +161,12 @@ public class SubscriptionManager: NSObject {
fileprivate static let lastSubscriptionHeartbeatKey = "subscriptionHeartbeat"
fileprivate static let lastSubscriptionReceiptRedemptionFailedKey = "lastSubscriptionReceiptRedemptionFailedKey"
fileprivate static let userManuallyCancelledSubscriptionKey = "userManuallyCancelledSubscriptionKey"
fileprivate static let displayBadgesOnProfileKey = "displayBadgesOnProfileKey"
fileprivate static let knownUserSubscriptionBadgeIDsKey = "knownUserSubscriptionBadgeIDsKey"
fileprivate static let knownUserBoostBadgeIDsKey = "knownUserBoostBadgeIDsKey"
fileprivate static let mostRecentlyExpiredBadgeIDKey = "mostRecentlyExpiredBadgeIDKey"
fileprivate static let showExpirySheetOnHomeScreenKey = "showExpirySheetOnHomeScreenKey"
fileprivate static let hasMigratedToStorageServiceKey = "hasMigratedToStorageServiceKey"
public static var terminateTransactionIfPossible = false
@ -820,8 +857,26 @@ extension SubscriptionManager {
return subscriptionKVS.getBool(userManuallyCancelledSubscriptionKey, transaction: transaction) ?? false
}
private static func setUserManuallyCancelledSubscription(_ userCancelled: Bool, transaction: SDSAnyWriteTransaction) {
subscriptionKVS.setBool(userCancelled, key: userManuallyCancelledSubscriptionKey, transaction: transaction)
private static func setUserManuallyCancelledSubscription(_ value: Bool, updateStorageService: Bool = false, transaction: SDSAnyWriteTransaction) {
guard value != userManuallyCancelledSubscription(transaction: transaction) else { return }
subscriptionKVS.setBool(value, key: userManuallyCancelledSubscriptionKey, transaction: transaction)
if updateStorageService {
storageServiceManager.recordPendingLocalAccountUpdates()
}
}
private static func displayBadgesOnProfile(transaction: SDSAnyReadTransaction) -> Bool {
return subscriptionKVS.getBool(displayBadgesOnProfileKey, transaction: transaction) ?? false
}
private static var displayBadgesOnProfileCache = AtomicBool(false)
private static func setDisplayBadgesOnProfile(_ value: Bool, updateStorageService: Bool = false, transaction: SDSAnyWriteTransaction) {
guard value != displayBadgesOnProfile(transaction: transaction) else { return }
displayBadgesOnProfileCache.set(value)
subscriptionKVS.setBool(value, key: displayBadgesOnProfileKey, transaction: transaction)
if updateStorageService {
storageServiceManager.recordPendingLocalAccountUpdates()
}
}
fileprivate static func lastSubscriptionExpirationDate(transaction: SDSAnyReadTransaction) -> Date? {
@ -1081,6 +1136,20 @@ extension SubscriptionManager: SubscriptionManagerProtocol {
var expiringBadgeId = Self.mostRecentlyExpiredBadgeID(transaction: transaction)
var userManuallyCancelled = Self.userManuallyCancelledSubscription(transaction: transaction)
var showExpiryOnHomeScreen = Self.showExpirySheetOnHomeScreenKey(transaction: transaction)
var displayBadgesOnProfile = Self.displayBadgesOnProfile(transaction: transaction)
if !currentBadges.isEmpty {
let isCurrentlyDisplayingBadgesOnProfile = currentBadges.allSatisfy { badge in
badge.isVisible ?? {
owsFailDebug("Local user badges should always have a non-nil visibility flag")
return true
}()
}
if displayBadgesOnProfile != isCurrentlyDisplayingBadgesOnProfile {
displayBadgesOnProfile = isCurrentlyDisplayingBadgesOnProfile
Logger.info("Updating displayBadgesOnProfile to reflect state on profile \(displayBadgesOnProfile)")
}
}
let newSubscriberBadgeIds = Set(currentSubscriberBadgeIDs).subtracting(persistedSubscriberBadgeIDs)
Logger.info("Learned of \(newSubscriberBadgeIds.count) new subscriber badge ids: \(newSubscriberBadgeIds)")
@ -1141,6 +1210,7 @@ extension SubscriptionManager: SubscriptionManagerProtocol {
Most Recently Expired Badge Id: \(expiringBadgeId ?? "nil")
Show Expiry On Home Screen: \(showExpiryOnHomeScreen)
User Manually Cancelled Subscription: \(userManuallyCancelled)
Display Badges On Profile: \(displayBadgesOnProfile)
""")
// Persist new values
@ -1149,6 +1219,7 @@ extension SubscriptionManager: SubscriptionManagerProtocol {
Self.setMostRecentlyExpiredBadgeID(badgeID: expiringBadgeId, transaction: transaction)
Self.setShowExpirySheetOnHomeScreenKey(show: showExpiryOnHomeScreen, transaction: transaction)
Self.setUserManuallyCancelledSubscription(userManuallyCancelled, transaction: transaction)
Self.setDisplayBadgesOnProfile(displayBadgesOnProfile, transaction: transaction)
}
public func hasCurrentSubscription(transaction: SDSAnyReadTransaction) -> Bool {
@ -1162,4 +1233,22 @@ extension SubscriptionManager: SubscriptionManagerProtocol {
return lastSubscriptionExpiryDate.isAfterNow
}
public func userManuallyCancelledSubscription(transaction: SDSAnyReadTransaction) -> Bool {
return Self.userManuallyCancelledSubscription(transaction: transaction)
}
public func setUserManuallyCancelledSubscription(_ userCancelled: Bool, updateStorageService: Bool, transaction: SDSAnyWriteTransaction) {
Self.setUserManuallyCancelledSubscription(userCancelled, updateStorageService: updateStorageService, transaction: transaction)
}
public var displayBadgesOnProfile: Bool { Self.displayBadgesOnProfileCache.get() }
public func displayBadgesOnProfile(transaction: SDSAnyReadTransaction) -> Bool {
return Self.displayBadgesOnProfile(transaction: transaction)
}
public func setDisplayBadgesOnProfile(_ displayBadgesOnProfile: Bool, updateStorageService: Bool, transaction: SDSAnyWriteTransaction) {
Self.setDisplayBadgesOnProfile(displayBadgesOnProfile, updateStorageService: updateStorageService, transaction: transaction)
}
}

View File

@ -345,11 +345,6 @@ const NSString *kNSNotificationKey_UserProfileWriter = @"kNSNotificationKey_User
return [self loadProfileAvatarDataWithFilename:filename];
}
- (BOOL)localProfileHasVisibleBadge
{
return self.localUserProfile.visibleBadges.count > 0;
}
- (nullable NSString *)localUsername
{
return self.localUserProfile.username;

View File

@ -135,23 +135,25 @@ message AccountRecord {
bytes paymentsEntropy = 2;
}
bytes profileKey = 1;
string givenName = 2;
string familyName = 3;
string avatarUrl = 4;
bool noteToSelfArchived = 5;
bool readReceipts = 6;
bool sealedSenderIndicators = 7;
bool typingIndicators = 8;
bool proxiedLinkPreviews = 9; // Legacy link previews flag
bool noteToSelfMarkedUnread = 10;
bool linkPreviews = 11;
PhoneNumberSharingMode phoneNumberSharingMode = 12;
bool notDiscoverableByPhoneNumber = 13;
repeated PinnedConversation pinnedConversations = 14;
bool preferContactAvatars = 15;
Payments payments = 16;
uint32 universalExpireTimer = 17;
bytes subscriberID = 21;
string subscriberCurrencyCode = 22;
bytes profileKey = 1;
string givenName = 2;
string familyName = 3;
string avatarUrl = 4;
bool noteToSelfArchived = 5;
bool readReceipts = 6;
bool sealedSenderIndicators = 7;
bool typingIndicators = 8;
bool proxiedLinkPreviews = 9; // Legacy link previews flag
bool noteToSelfMarkedUnread = 10;
bool linkPreviews = 11;
PhoneNumberSharingMode phoneNumberSharingMode = 12;
bool notDiscoverableByPhoneNumber = 13;
repeated PinnedConversation pinnedConversations = 14;
bool preferContactAvatars = 15;
Payments payments = 16;
uint32 universalExpireTimer = 17;
bytes subscriberID = 21;
string subscriberCurrencyCode = 22;
bool displayBadgesOnProfile = 23;
bool subscriptionManuallyCancelled = 24;
}

View File

@ -999,7 +999,7 @@ NSString *const OWSRequestKey_AuthKey = @"AuthKey";
method:@"POST"
parameters:@{
@"receiptCredentialPresentation" : base64ReceiptCredentialPresentation,
@"visible" : @(self.profileManager.localProfileHasVisibleBadge),
@"visible" : @(self.subscriptionManager.displayBadgesOnProfile),
@"primary" : @(NO)
}];
return request;

View File

@ -50,8 +50,6 @@ typedef NS_ENUM(NSUInteger, UserProfileWriter) {
- (nullable UIImage *)localProfileAvatarImage;
- (nullable NSData *)localProfileAvatarData;
@property (nonatomic, readonly) BOOL localProfileHasVisibleBadge;
- (nullable NSString *)fullNameForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction;

View File

@ -8,4 +8,10 @@ import Foundation
public protocol SubscriptionManagerProtocol {
func reconcileBadgeStates(transaction: SDSAnyWriteTransaction)
func hasCurrentSubscription(transaction: SDSAnyReadTransaction) -> Bool
func userManuallyCancelledSubscription(transaction: SDSAnyReadTransaction) -> Bool
func setUserManuallyCancelledSubscription(_ userCancelled: Bool, updateStorageService: Bool, transaction: SDSAnyWriteTransaction)
var displayBadgesOnProfile: Bool { get }
func displayBadgesOnProfile(transaction: SDSAnyReadTransaction) -> Bool
func setDisplayBadgesOnProfile(_ displayBadgesOnProfile: Bool, updateStorageService: Bool, transaction: SDSAnyWriteTransaction)
}

View File

@ -495,6 +495,16 @@ struct StorageServiceProtos_AccountRecord {
set {_uniqueStorage()._subscriberCurrencyCode = newValue}
}
var displayBadgesOnProfile: Bool {
get {return _storage._displayBadgesOnProfile}
set {_uniqueStorage()._displayBadgesOnProfile = newValue}
}
var subscriptionManuallyCancelled: Bool {
get {return _storage._subscriptionManuallyCancelled}
set {_uniqueStorage()._subscriptionManuallyCancelled = newValue}
}
var unknownFields = SwiftProtobuf.UnknownStorage()
enum PhoneNumberSharingMode: SwiftProtobuf.Enum {
@ -1286,7 +1296,9 @@ extension StorageServiceProtos_AccountRecord: SwiftProtobuf.Message, SwiftProtob
16: .same(proto: "payments"),
17: .same(proto: "universalExpireTimer"),
21: .same(proto: "subscriberID"),
22: .same(proto: "subscriberCurrencyCode")
22: .same(proto: "subscriberCurrencyCode"),
23: .same(proto: "displayBadgesOnProfile"),
24: .same(proto: "subscriptionManuallyCancelled")
]
fileprivate class _StorageClass {
@ -1309,6 +1321,8 @@ extension StorageServiceProtos_AccountRecord: SwiftProtobuf.Message, SwiftProtob
var _universalExpireTimer: UInt32 = 0
var _subscriberID: Data = Data()
var _subscriberCurrencyCode: String = String()
var _displayBadgesOnProfile: Bool = false
var _subscriptionManuallyCancelled: Bool = false
static let defaultInstance = _StorageClass()
@ -1334,6 +1348,8 @@ extension StorageServiceProtos_AccountRecord: SwiftProtobuf.Message, SwiftProtob
_universalExpireTimer = source._universalExpireTimer
_subscriberID = source._subscriberID
_subscriberCurrencyCode = source._subscriberCurrencyCode
_displayBadgesOnProfile = source._displayBadgesOnProfile
_subscriptionManuallyCancelled = source._subscriptionManuallyCancelled
}
}
@ -1371,6 +1387,8 @@ extension StorageServiceProtos_AccountRecord: SwiftProtobuf.Message, SwiftProtob
case 17: try { try decoder.decodeSingularUInt32Field(value: &_storage._universalExpireTimer) }()
case 21: try { try decoder.decodeSingularBytesField(value: &_storage._subscriberID) }()
case 22: try { try decoder.decodeSingularStringField(value: &_storage._subscriberCurrencyCode) }()
case 23: try { try decoder.decodeSingularBoolField(value: &_storage._displayBadgesOnProfile) }()
case 24: try { try decoder.decodeSingularBoolField(value: &_storage._subscriptionManuallyCancelled) }()
default: break
}
}
@ -1440,6 +1458,12 @@ extension StorageServiceProtos_AccountRecord: SwiftProtobuf.Message, SwiftProtob
if !_storage._subscriberCurrencyCode.isEmpty {
try visitor.visitSingularStringField(value: _storage._subscriberCurrencyCode, fieldNumber: 22)
}
if _storage._displayBadgesOnProfile != false {
try visitor.visitSingularBoolField(value: _storage._displayBadgesOnProfile, fieldNumber: 23)
}
if _storage._subscriptionManuallyCancelled != false {
try visitor.visitSingularBoolField(value: _storage._subscriptionManuallyCancelled, fieldNumber: 24)
}
}
try unknownFields.traverse(visitor: &visitor)
}
@ -1468,6 +1492,8 @@ extension StorageServiceProtos_AccountRecord: SwiftProtobuf.Message, SwiftProtob
if _storage._universalExpireTimer != rhs_storage._universalExpireTimer {return false}
if _storage._subscriberID != rhs_storage._subscriberID {return false}
if _storage._subscriberCurrencyCode != rhs_storage._subscriberCurrencyCode {return false}
if _storage._displayBadgesOnProfile != rhs_storage._displayBadgesOnProfile {return false}
if _storage._subscriptionManuallyCancelled != rhs_storage._subscriptionManuallyCancelled {return false}
return true
}
if !storagesAreEqual {return false}

View File

@ -2562,6 +2562,12 @@ public struct StorageServiceProtoAccountRecord: Codable, CustomDebugStringConver
if let _value = subscriberCurrencyCode {
builder.setSubscriberCurrencyCode(_value)
}
if hasDisplayBadgesOnProfile {
builder.setDisplayBadgesOnProfile(displayBadgesOnProfile)
}
if hasSubscriptionManuallyCancelled {
builder.setSubscriptionManuallyCancelled(subscriptionManuallyCancelled)
}
if let _value = unknownFields {
builder.setUnknownFields(_value)
}
@ -2696,6 +2702,14 @@ public struct StorageServiceProtoAccountRecord: Codable, CustomDebugStringConver
proto.subscriberCurrencyCode = valueParam
}
public mutating func setDisplayBadgesOnProfile(_ valueParam: Bool) {
proto.displayBadgesOnProfile = valueParam
}
public mutating func setSubscriptionManuallyCancelled(_ valueParam: Bool) {
proto.subscriptionManuallyCancelled = valueParam
}
public mutating func setUnknownFields(_ unknownFields: SwiftProtobuf.UnknownStorage) {
proto.unknownFields = unknownFields
}
@ -2863,6 +2877,20 @@ public struct StorageServiceProtoAccountRecord: Codable, CustomDebugStringConver
return !proto.subscriberCurrencyCode.isEmpty
}
public var displayBadgesOnProfile: Bool {
return proto.displayBadgesOnProfile
}
public var hasDisplayBadgesOnProfile: Bool {
return true
}
public var subscriptionManuallyCancelled: Bool {
return proto.subscriptionManuallyCancelled
}
public var hasSubscriptionManuallyCancelled: Bool {
return true
}
public var hasUnknownFields: Bool {
return !proto.unknownFields.data.isEmpty
}

View File

@ -12,4 +12,10 @@ public class MockSubscriptionManager: NSObject, SubscriptionManagerProtocol {
public func hasCurrentSubscription(transaction: SDSAnyReadTransaction) -> Bool {
return false
}
public func userManuallyCancelledSubscription(transaction: SDSAnyReadTransaction) -> Bool { false }
public func setUserManuallyCancelledSubscription(_ userCancelled: Bool, updateStorageService: Bool, transaction: SDSAnyWriteTransaction) {}
public var displayBadgesOnProfile: Bool { false }
public func displayBadgesOnProfile(transaction: SDSAnyReadTransaction) -> Bool { false }
public func setDisplayBadgesOnProfile(_ displayBadgesOnProfile: Bool, updateStorageService: Bool, transaction: SDSAnyWriteTransaction) {}
}

View File

@ -21,7 +21,6 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, nullable) NSString *localGivenName;
@property (nonatomic, nullable) NSString *localFamilyName;
@property (nonatomic, nullable) NSString *localFullName;
@property (nonatomic) BOOL localProfileHasVisibleBadge;
@property (nonatomic, nullable) NSString *localUsername;
@property (nonatomic, nullable) NSData *localProfileAvatarData;