diff --git a/damus/Components/DamusColors.swift b/damus/Components/DamusColors.swift index 50563d74..1d6e0f64 100644 --- a/damus/Components/DamusColors.swift +++ b/damus/Components/DamusColors.swift @@ -16,6 +16,7 @@ class DamusColors { static let black = Color("DamusBlack") static let brown = Color("DamusBrown") static let yellow = Color("DamusYellow") + static let gold = hex_col(r: 226, g: 168, b: 0) static let lightGrey = Color("DamusLightGrey") static let mediumGrey = Color("DamusMediumGrey") static let darkGrey = Color("DamusDarkGrey") @@ -46,3 +47,10 @@ class DamusColors { static let lightBackgroundPink = Color(red: 0xF8/255.0, green: 0xE7/255.0, blue: 0xF8/255.0) } +func hex_col(r: UInt8, g: UInt8, b: UInt8) -> Color { + return Color(.sRGB, + red: Double(r) / Double(0xff), + green: Double(g) / Double(0xff), + blue: Double(b) / Double(0xff), + opacity: 1.0) +} diff --git a/damus/Components/Gradients/GoldSupportGradient.swift b/damus/Components/Gradients/GoldSupportGradient.swift index 3e255cd1..7fe2bdb4 100644 --- a/damus/Components/Gradients/GoldSupportGradient.swift +++ b/damus/Components/Gradients/GoldSupportGradient.swift @@ -7,7 +7,7 @@ import SwiftUI -fileprivate let gold_grad_c1 = hex_col(r: 226, g: 168, b: 0) +fileprivate let gold_grad_c1 = DamusColors.gold fileprivate let gold_grad_c2 = hex_col(r: 249, g: 243, b: 100) fileprivate let gold_grad = [gold_grad_c2, gold_grad_c1] diff --git a/damus/Components/SupporterBadge.swift b/damus/Components/SupporterBadge.swift index 1af67eee..a90b227d 100644 --- a/damus/Components/SupporterBadge.swift +++ b/damus/Components/SupporterBadge.swift @@ -8,23 +8,53 @@ import SwiftUI struct SupporterBadge: View { - let percent: Int + let percent: Int? + let purple_badge_info: DamusPurple.UserBadgeInfo? + let style: Style + + init(percent: Int?, purple_badge_info: DamusPurple.UserBadgeInfo? = nil, style: Style) { + self.percent = percent + self.purple_badge_info = purple_badge_info + self.style = style + } let size: CGFloat = 17 var body: some View { - if percent < 100 { - Image("star.fill") - .resizable() - .frame(width:size, height:size) - .foregroundColor(support_level_color(percent)) - } else { - Image("star.fill") - .resizable() - .frame(width:size, height:size) - .foregroundStyle(GoldGradient) + HStack { + if let purple_badge_info, purple_badge_info.active == true { + HStack(spacing: 1) { + Image("star.fill") + .resizable() + .frame(width:size, height:size) + .foregroundStyle(GoldGradient) + if self.style == .full, + let ordinal_number = self.purple_badge_info?.subscriber_number, + let ordinal = self.purple_badge_info?.ordinal() { + Text(ordinal) + .foregroundStyle(DamusColors.gold) + .font(.caption) + } + } + } + else if let percent, percent < 100 { + Image("star.fill") + .resizable() + .frame(width:size, height:size) + .foregroundColor(support_level_color(percent)) + } else if let percent, percent == 100 { + Image("star.fill") + .resizable() + .frame(width:size, height:size) + .foregroundStyle(GoldGradient) + } } } + + enum Style { + case full // Shows the entire badge with a purple subscriber number if present + case compact // Does not show purple subscriber number. Only shows the star (if applicable) + } } func support_level_color(_ percent: Int) -> Color { @@ -44,13 +74,24 @@ func support_level_color(_ percent: Int) -> Color { struct SupporterBadge_Previews: PreviewProvider { static func Level(_ p: Int) -> some View { HStack(alignment: .center) { - SupporterBadge(percent: p) + SupporterBadge(percent: p, style: .full) .frame(width: 50) Text(verbatim: p.formatted()) .frame(width: 50) } } + static func Purple(_ subscriber_number: Int) -> some View { + HStack(alignment: .center) { + SupporterBadge( + percent: nil, + purple_badge_info: DamusPurple.UserBadgeInfo(active: true, subscriber_number: subscriber_number), + style: .full + ) + .frame(width: 100) + } + } + static var previews: some View { VStack(spacing: 0) { VStack(spacing: 0) { @@ -66,6 +107,12 @@ struct SupporterBadge_Previews: PreviewProvider { Level(80) Level(90) Level(100) + Purple(1) + Purple(2) + Purple(3) + Purple(99) + Purple(100) + Purple(1971) } } } diff --git a/damus/Models/Purple/DamusPurple.swift b/damus/Models/Purple/DamusPurple.swift index b91a3584..b07e34f6 100644 --- a/damus/Models/Purple/DamusPurple.swift +++ b/damus/Models/Purple/DamusPurple.swift @@ -10,7 +10,7 @@ import Foundation class DamusPurple: StoreObserverDelegate { let environment: ServerEnvironment let keypair: Keypair - var starred_profiles_cache: [Pubkey: Bool] + var starred_profiles_cache: [Pubkey: UserBadgeInfo] init(environment: ServerEnvironment, keypair: Keypair) { self.environment = environment @@ -20,16 +20,23 @@ class DamusPurple: StoreObserverDelegate { // MARK: Functions func is_profile_subscribed_to_purple(pubkey: Pubkey) async -> Bool? { + return await self.profile_purple_badge_info(pubkey: pubkey)?.active + } + + func profile_purple_badge_info(pubkey: Pubkey) async -> UserBadgeInfo? { if let cached_result = self.starred_profiles_cache[pubkey] { return cached_result } guard let data = await self.get_account_data(pubkey: pubkey) else { return nil } - if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], - let active = json["active"] as? Bool { - self.starred_profiles_cache[pubkey] = active - return active + 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 @@ -180,6 +187,18 @@ 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)) + } + } } // MARK: API types diff --git a/damus/Views/Profile/EventProfileName.swift b/damus/Views/Profile/EventProfileName.swift index 5a31822b..6b61cc69 100644 --- a/damus/Views/Profile/EventProfileName.swift +++ b/damus/Views/Profile/EventProfileName.swift @@ -16,7 +16,7 @@ struct EventProfileName: View { @State var display_name: DisplayName? @State var nip05: NIP05? @State var donation: Int? - @State var is_purple_user: Bool? + @State var purple_badge: DamusPurple.UserBadgeInfo? let size: EventViewKind @@ -26,7 +26,7 @@ struct EventProfileName: View { self.size = size let donation = damus.ndb.lookup_profile(pubkey)?.map({ p in p?.profile?.damus_donation }).value self._donation = State(wrappedValue: donation) - is_purple_user = nil + self.purple_badge = nil } var friend_type: FriendType? { @@ -50,11 +50,6 @@ struct EventProfileName: View { } func supporter_percentage() -> Int? { - if damus_state.settings.enable_experimental_purple_api, - is_purple_user == true { - return 100 - } - guard let donation, donation > 0 else { return nil @@ -99,9 +94,7 @@ struct EventProfileName: View { .frame(width: 14, height: 14) } - if let supporter = self.supporter_percentage() { - SupporterBadge(percent: supporter) - } + SupporterBadge(percent: self.supporter_percentage(), purple_badge_info: self.purple_badge, style: .compact) } .onReceive(handle_notify(.profile_updated)) { update in if update.pubkey != pubkey { @@ -129,7 +122,7 @@ struct EventProfileName: View { .onAppear(perform: { Task { if damus_state.settings.enable_experimental_purple_api { - is_purple_user = await damus_state.purple.is_profile_subscribed_to_purple(pubkey: self.pubkey) ?? false + self.purple_badge = await damus_state.purple.profile_purple_badge_info(pubkey: pubkey) } } }) diff --git a/damus/Views/Profile/ProfileName.swift b/damus/Views/Profile/ProfileName.swift index 92b8cdbe..5f4eb198 100644 --- a/damus/Views/Profile/ProfileName.swift +++ b/damus/Views/Profile/ProfileName.swift @@ -41,12 +41,14 @@ struct ProfileName: View { @State var display_name: DisplayName? @State var nip05: NIP05? @State var donation: Int? + @State var purple_badge: DamusPurple.UserBadgeInfo? init(pubkey: Pubkey, prefix: String = "", damus: DamusState, show_nip5_domain: Bool = true) { self.pubkey = pubkey self.prefix = prefix self.damus_state = damus self.show_nip5_domain = show_nip5_domain + self.purple_badge = nil } var friend_type: FriendType? { @@ -107,10 +109,15 @@ struct ProfileName: View { .frame(width: 14, height: 14) } - if let supporter = supporter(profile: profile) { - SupporterBadge(percent: supporter) - } + SupporterBadge(percent: supporter(profile: profile), purple_badge_info: self.purple_badge, style: .full) } + .onAppear(perform: { + Task { + if damus_state.settings.enable_experimental_purple_api { + self.purple_badge = await damus_state.purple.profile_purple_badge_info(pubkey: pubkey) + } + } + }) .onReceive(handle_notify(.profile_updated)) { update in if update.pubkey != pubkey { return diff --git a/damus/Views/SetupView.swift b/damus/Views/SetupView.swift index 3ecbb842..7ae4091d 100644 --- a/damus/Views/SetupView.swift +++ b/damus/Views/SetupView.swift @@ -7,14 +7,6 @@ import SwiftUI -func hex_col(r: UInt8, g: UInt8, b: UInt8) -> Color { - return Color(.sRGB, - red: Double(r) / Double(0xff), - green: Double(g) / Double(0xff), - blue: Double(b) / Double(0xff), - opacity: 1.0) -} - struct SetupView: View { @StateObject var navigationCoordinator: NavigationCoordinator = NavigationCoordinator()