purple: consolidate UserBadgeInfo with Account

Rename get_account to fetch_account to make it clear that it is always a
call to the server.

Add get_maybe_cached_account method that checks cached before calling
fetch_account.

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2024-01-30 17:30:12 -08:00
parent fe177bdb9e
commit f06b882139
7 changed files with 59 additions and 77 deletions

View File

@@ -9,12 +9,12 @@ import SwiftUI
struct SupporterBadge: View { struct SupporterBadge: View {
let percent: Int? let percent: Int?
let purple_badge_info: DamusPurple.UserBadgeInfo? let purple_account: DamusPurple.Account?
let style: Style let style: Style
init(percent: Int?, purple_badge_info: DamusPurple.UserBadgeInfo? = nil, style: Style) { init(percent: Int?, purple_account: DamusPurple.Account? = nil, style: Style) {
self.percent = percent self.percent = percent
self.purple_badge_info = purple_badge_info self.purple_account = purple_account
self.style = style self.style = style
} }
@@ -22,15 +22,15 @@ struct SupporterBadge: View {
var body: some View { var body: some View {
HStack { HStack {
if let purple_badge_info, purple_badge_info.active == true { if let purple_account, purple_account.active == true {
HStack(spacing: 1) { HStack(spacing: 1) {
Image("star.fill") Image("star.fill")
.resizable() .resizable()
.frame(width:size, height:size) .frame(width:size, height:size)
.foregroundStyle(GoldGradient) .foregroundStyle(GoldGradient)
if self.style == .full, if self.style == .full,
let ordinal_number = self.purple_badge_info?.subscriber_number, let ordinal_number = self.purple_account?.subscriber_number,
let ordinal = self.purple_badge_info?.ordinal() { let ordinal = self.purple_account?.ordinal() {
Text(ordinal) Text(ordinal)
.foregroundStyle(DamusColors.gold) .foregroundStyle(DamusColors.gold)
.font(.caption) .font(.caption)
@@ -85,7 +85,7 @@ struct SupporterBadge_Previews: PreviewProvider {
HStack(alignment: .center) { HStack(alignment: .center) {
SupporterBadge( SupporterBadge(
percent: nil, percent: nil,
purple_badge_info: DamusPurple.UserBadgeInfo(active: true, subscriber_number: subscriber_number), purple_account: DamusPurple.Account(pubkey: test_pubkey, created_at: .now, expiry: .now.addingTimeInterval(10000), subscriber_number: subscriber_number, active: true),
style: .full style: .full
) )
.frame(width: 100) .frame(width: 100)

View File

@@ -10,17 +10,17 @@ import Foundation
class DamusPurple: StoreObserverDelegate { class DamusPurple: StoreObserverDelegate {
let settings: UserSettingsStore let settings: UserSettingsStore
let keypair: Keypair let keypair: Keypair
var starred_profiles_cache: [Pubkey: UserBadgeInfo] var account_cache: [Pubkey: Account]
init(settings: UserSettingsStore, keypair: Keypair) { init(settings: UserSettingsStore, keypair: Keypair) {
self.settings = settings self.settings = settings
self.keypair = keypair self.keypair = keypair
self.starred_profiles_cache = [:] self.account_cache = [:]
} }
// MARK: Functions // MARK: Functions
func is_profile_subscribed_to_purple(pubkey: Pubkey) async -> Bool? { func is_profile_subscribed_to_purple(pubkey: Pubkey) async -> Bool? {
return await self.profile_purple_badge_info(pubkey: pubkey)?.active return try? await self.get_maybe_cached_account(pubkey: pubkey)?.active
} }
var environment: DamusPurpleEnvironment { var environment: DamusPurpleEnvironment {
@@ -38,25 +38,6 @@ class DamusPurple: StoreObserverDelegate {
return self.settings.enable_experimental_purple_iap_support return self.settings.enable_experimental_purple_iap_support
} }
func profile_purple_badge_info(pubkey: Pubkey) async -> UserBadgeInfo? {
if let cached_result = self.starred_profiles_cache[pubkey] {
return cached_result
}
guard let data = try? await self.get_account_data(pubkey: pubkey) else { return nil }
guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { return nil }
if let active = json["active"] as? Bool {
let subscriber_number: Int? = json["subscriber_number"] as? Int
let badge_info = UserBadgeInfo(active: active, subscriber_number: subscriber_number)
self.starred_profiles_cache[pubkey] = badge_info
return badge_info
}
return nil
}
func account_exists(pubkey: Pubkey) async -> Bool? { func account_exists(pubkey: Pubkey) async -> Bool? {
guard let account_data = try? await self.get_account_data(pubkey: pubkey) else { return nil } guard let account_data = try? await self.get_account_data(pubkey: pubkey) else { return nil }
@@ -67,9 +48,20 @@ class DamusPurple: StoreObserverDelegate {
return false return false
} }
func get_account(pubkey: Pubkey) async throws -> Account? { func get_maybe_cached_account(pubkey: Pubkey) async throws -> Account? {
guard let data = try await self.get_account_data(pubkey: pubkey) else { return nil } if let account = self.account_cache[pubkey] {
return Account.from(json_data: data) return account
}
return try await fetch_account(pubkey: pubkey)
}
func fetch_account(pubkey: Pubkey) async throws -> Account? {
guard let data = try await self.get_account_data(pubkey: pubkey) ,
let account = Account.from(json_data: data) else {
return nil
}
self.account_cache[pubkey] = account
return account
} }
func get_account_data(pubkey: Pubkey) async throws -> Data? { func get_account_data(pubkey: Pubkey) async throws -> Data? {
@@ -217,29 +209,20 @@ class DamusPurple: StoreObserverDelegate {
} }
struct UserBadgeInfo {
var active: Bool
var subscriber_number: Int?
func ordinal() -> String? {
guard let number = self.subscriber_number else { return nil }
let formatter = NumberFormatter()
formatter.numberStyle = .ordinal
return formatter.string(from: NSNumber(integerLiteral: number))
}
static func from(account: Account) -> Self {
return UserBadgeInfo(active: account.active, subscriber_number: Int(account.subscriber_number))
}
}
struct Account { struct Account {
let pubkey: Pubkey let pubkey: Pubkey
let created_at: Date let created_at: Date
let expiry: Date let expiry: Date
let subscriber_number: UInt let subscriber_number: Int
let active: Bool let active: Bool
func ordinal() -> String? {
let number = Int(self.subscriber_number)
let formatter = NumberFormatter()
formatter.numberStyle = .ordinal
return formatter.string(from: NSNumber(integerLiteral: number))
}
static func from(json_data: Data) -> Self? { static func from(json_data: Data) -> Self? {
guard let payload = try? JSONDecoder().decode(Payload.self, from: json_data) else { return nil } guard let payload = try? JSONDecoder().decode(Payload.self, from: json_data) else { return nil }
return Self.from(payload: payload) return Self.from(payload: payload)
@@ -251,7 +234,7 @@ class DamusPurple: StoreObserverDelegate {
pubkey: pubkey, pubkey: pubkey,
created_at: Date.init(timeIntervalSince1970: TimeInterval(payload.created_at)), created_at: Date.init(timeIntervalSince1970: TimeInterval(payload.created_at)),
expiry: Date.init(timeIntervalSince1970: TimeInterval(payload.expiry)), expiry: Date.init(timeIntervalSince1970: TimeInterval(payload.expiry)),
subscriber_number: payload.subscriber_number, subscriber_number: Int(payload.subscriber_number),
active: payload.active active: payload.active
) )
} }

View File

@@ -16,7 +16,7 @@ struct EventProfileName: View {
@State var display_name: DisplayName? @State var display_name: DisplayName?
@State var nip05: NIP05? @State var nip05: NIP05?
@State var donation: Int? @State var donation: Int?
@State var purple_badge: DamusPurple.UserBadgeInfo? @State var purple_account: DamusPurple.Account?
let size: EventViewKind let size: EventViewKind
@@ -26,7 +26,7 @@ struct EventProfileName: View {
self.size = size self.size = size
let donation = damus.ndb.lookup_profile(pubkey)?.map({ p in p?.profile?.damus_donation }).value let donation = damus.ndb.lookup_profile(pubkey)?.map({ p in p?.profile?.damus_donation }).value
self._donation = State(wrappedValue: donation) self._donation = State(wrappedValue: donation)
self.purple_badge = nil self.purple_account = nil
} }
var friend_type: FriendType? { var friend_type: FriendType? {
@@ -94,7 +94,7 @@ struct EventProfileName: View {
.frame(width: 14, height: 14) .frame(width: 14, height: 14)
} }
SupporterBadge(percent: self.supporter_percentage(), purple_badge_info: self.purple_badge, style: .compact) SupporterBadge(percent: self.supporter_percentage(), purple_account: self.purple_account, style: .compact)
} }
.onReceive(handle_notify(.profile_updated)) { update in .onReceive(handle_notify(.profile_updated)) { update in
if update.pubkey != pubkey { if update.pubkey != pubkey {
@@ -119,13 +119,11 @@ struct EventProfileName: View {
donation = profile.damus_donation donation = profile.damus_donation
} }
} }
.onAppear(perform: { .task {
Task { if damus_state.purple.enable_purple {
if damus_state.purple.enable_purple { self.purple_account = try? await damus_state.purple.get_maybe_cached_account(pubkey: pubkey)
self.purple_badge = await damus_state.purple.profile_purple_badge_info(pubkey: pubkey)
}
} }
}) }
} }
} }

View File

@@ -41,14 +41,14 @@ struct ProfileName: View {
@State var display_name: DisplayName? @State var display_name: DisplayName?
@State var nip05: NIP05? @State var nip05: NIP05?
@State var donation: Int? @State var donation: Int?
@State var purple_badge: DamusPurple.UserBadgeInfo? @State var purple_account: DamusPurple.Account?
init(pubkey: Pubkey, prefix: String = "", damus: DamusState, show_nip5_domain: Bool = true) { init(pubkey: Pubkey, prefix: String = "", damus: DamusState, show_nip5_domain: Bool = true) {
self.pubkey = pubkey self.pubkey = pubkey
self.prefix = prefix self.prefix = prefix
self.damus_state = damus self.damus_state = damus
self.show_nip5_domain = show_nip5_domain self.show_nip5_domain = show_nip5_domain
self.purple_badge = nil self.purple_account = nil
} }
var friend_type: FriendType? { var friend_type: FriendType? {
@@ -109,15 +109,15 @@ struct ProfileName: View {
.frame(width: 14, height: 14) .frame(width: 14, height: 14)
} }
SupporterBadge(percent: supporter(profile: profile), purple_badge_info: self.purple_badge, style: .full) SupporterBadge(percent: supporter(profile: profile), purple_account: self.purple_account, style: .compact)
} }
.onAppear(perform: { .task {
Task { if damus_state.purple.enable_purple {
if damus_state.purple.enable_purple { self.purple_account = try? await damus_state.purple.get_maybe_cached_account(pubkey: pubkey)
self.purple_badge = await damus_state.purple.profile_purple_badge_info(pubkey: pubkey)
}
} }
}) }
.onReceive(handle_notify(.profile_updated)) { update in .onReceive(handle_notify(.profile_updated)) { update in
if update.pubkey != pubkey { if update.pubkey != pubkey {
return return

View File

@@ -83,7 +83,7 @@ struct DamusPurpleAccountView: View {
SupporterBadge( SupporterBadge(
percent: nil, percent: nil,
purple_badge_info: DamusPurple.UserBadgeInfo.from(account: account), purple_account: account,
style: .full style: .full
) )
} }

View File

@@ -32,7 +32,7 @@ struct DamusPurpleNewUserOnboardingView: View {
.ignoresSafeArea() // Necessary to avoid weird white edges .ignoresSafeArea() // Necessary to avoid weird white edges
} }
.task { .task {
guard let account = try? await damus_state.purple.get_account(pubkey: damus_state.pubkey), account.active else { guard let account = try? await damus_state.purple.fetch_account(pubkey: damus_state.pubkey), account.active else {
return return
} }
// Let's notify other views across SwiftUI to update our user's Purple status. // Let's notify other views across SwiftUI to update our user's Purple status.

View File

@@ -117,7 +117,7 @@ struct DamusPurpleView: View {
func load_account() async { func load_account() async {
do { do {
if let account = try await damus_state.purple.get_account(pubkey: damus_state.keypair.pubkey) { if let account = try await damus_state.purple.fetch_account(pubkey: damus_state.keypair.pubkey) {
self.my_account_info_state = .loaded(account: account) self.my_account_info_state = .loaded(account: account)
return return
} }
@@ -232,7 +232,8 @@ struct DamusPurpleView: View {
switch result { switch result {
case .success: case .success:
self.damus_state.purple.starred_profiles_cache[keypair.pubkey] = nil // TODO (will): why do this here?
//self.damus_state.purple.starred_profiles_cache[keypair.pubkey] = nil
Task { Task {
await self.damus_state.purple.send_receipt() await self.damus_state.purple.send_receipt()
} }