mirror of
git://jb55.com/damus
synced 2024-09-29 08:20:45 +00:00
purple: add subscriber number on the Purple supporter badge
This change adds an subscriber number to the supporter badge. Testing -------- PASS Device: iPhone 15 simulator iOS: 17.2 Damus: This commit damus-api: `53fd7fef1c8c0bbf82bb28d1d776e45379433d23` Setup: - `damus-api` running on `npm run dev` mode - Damus configured to enable experimental Purple support + localhost mode Test steps: 1. Wipe local database and rerun server 2. Purchase purple using In-app purchase (Xcode environment) flow 3. Make sure that badge to the side of the user's name on the event shows a golden star without any ordinal numbers 4. Make sure that the badge to the side of the user's name on the profile page shows a golden star with the text "1st" 5. Repeat steps 2–4 and make sure the text says "2nd" 6. Look at the SupporterBadge view previews. Ordinals should show up correctly for all previews (They should always show up, no matter the number) Closes: https://github.com/damus-io/damus/issues/1873 Signed-off-by: Daniel D’Aquino <daniel@daquino.me> Reviewed-by: William Casarin <jb55@jb55.com> Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
parent
838ce26c64
commit
854036b413
@ -16,6 +16,7 @@ class DamusColors {
|
|||||||
static let black = Color("DamusBlack")
|
static let black = Color("DamusBlack")
|
||||||
static let brown = Color("DamusBrown")
|
static let brown = Color("DamusBrown")
|
||||||
static let yellow = Color("DamusYellow")
|
static let yellow = Color("DamusYellow")
|
||||||
|
static let gold = hex_col(r: 226, g: 168, b: 0)
|
||||||
static let lightGrey = Color("DamusLightGrey")
|
static let lightGrey = Color("DamusLightGrey")
|
||||||
static let mediumGrey = Color("DamusMediumGrey")
|
static let mediumGrey = Color("DamusMediumGrey")
|
||||||
static let darkGrey = Color("DamusDarkGrey")
|
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)
|
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)
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import SwiftUI
|
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_c2 = hex_col(r: 249, g: 243, b: 100)
|
||||||
|
|
||||||
fileprivate let gold_grad = [gold_grad_c2, gold_grad_c1]
|
fileprivate let gold_grad = [gold_grad_c2, gold_grad_c1]
|
||||||
|
@ -8,23 +8,53 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct SupporterBadge: View {
|
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
|
let size: CGFloat = 17
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if percent < 100 {
|
HStack {
|
||||||
Image("star.fill")
|
if let purple_badge_info, purple_badge_info.active == true {
|
||||||
.resizable()
|
HStack(spacing: 1) {
|
||||||
.frame(width:size, height:size)
|
Image("star.fill")
|
||||||
.foregroundColor(support_level_color(percent))
|
.resizable()
|
||||||
} else {
|
.frame(width:size, height:size)
|
||||||
Image("star.fill")
|
.foregroundStyle(GoldGradient)
|
||||||
.resizable()
|
if self.style == .full,
|
||||||
.frame(width:size, height:size)
|
let ordinal_number = self.purple_badge_info?.subscriber_number,
|
||||||
.foregroundStyle(GoldGradient)
|
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 {
|
func support_level_color(_ percent: Int) -> Color {
|
||||||
@ -44,13 +74,24 @@ func support_level_color(_ percent: Int) -> Color {
|
|||||||
struct SupporterBadge_Previews: PreviewProvider {
|
struct SupporterBadge_Previews: PreviewProvider {
|
||||||
static func Level(_ p: Int) -> some View {
|
static func Level(_ p: Int) -> some View {
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
SupporterBadge(percent: p)
|
SupporterBadge(percent: p, style: .full)
|
||||||
.frame(width: 50)
|
.frame(width: 50)
|
||||||
Text(verbatim: p.formatted())
|
Text(verbatim: p.formatted())
|
||||||
.frame(width: 50)
|
.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 {
|
static var previews: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
@ -66,6 +107,12 @@ struct SupporterBadge_Previews: PreviewProvider {
|
|||||||
Level(80)
|
Level(80)
|
||||||
Level(90)
|
Level(90)
|
||||||
Level(100)
|
Level(100)
|
||||||
|
Purple(1)
|
||||||
|
Purple(2)
|
||||||
|
Purple(3)
|
||||||
|
Purple(99)
|
||||||
|
Purple(100)
|
||||||
|
Purple(1971)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import Foundation
|
|||||||
class DamusPurple: StoreObserverDelegate {
|
class DamusPurple: StoreObserverDelegate {
|
||||||
let environment: ServerEnvironment
|
let environment: ServerEnvironment
|
||||||
let keypair: Keypair
|
let keypair: Keypair
|
||||||
var starred_profiles_cache: [Pubkey: Bool]
|
var starred_profiles_cache: [Pubkey: UserBadgeInfo]
|
||||||
|
|
||||||
init(environment: ServerEnvironment, keypair: Keypair) {
|
init(environment: ServerEnvironment, keypair: Keypair) {
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
@ -20,16 +20,23 @@ class DamusPurple: StoreObserverDelegate {
|
|||||||
|
|
||||||
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func profile_purple_badge_info(pubkey: Pubkey) async -> UserBadgeInfo? {
|
||||||
if let cached_result = self.starred_profiles_cache[pubkey] {
|
if let cached_result = self.starred_profiles_cache[pubkey] {
|
||||||
return cached_result
|
return cached_result
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let data = await self.get_account_data(pubkey: pubkey) else { return nil }
|
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],
|
guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { return nil }
|
||||||
let active = json["active"] as? Bool {
|
|
||||||
self.starred_profiles_cache[pubkey] = active
|
if let active = json["active"] as? Bool {
|
||||||
return active
|
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
|
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
|
// MARK: API types
|
||||||
|
@ -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 is_purple_user: Bool?
|
@State var purple_badge: DamusPurple.UserBadgeInfo?
|
||||||
|
|
||||||
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)
|
||||||
is_purple_user = nil
|
self.purple_badge = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var friend_type: FriendType? {
|
var friend_type: FriendType? {
|
||||||
@ -50,11 +50,6 @@ struct EventProfileName: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func supporter_percentage() -> Int? {
|
func supporter_percentage() -> Int? {
|
||||||
if damus_state.settings.enable_experimental_purple_api,
|
|
||||||
is_purple_user == true {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let donation, donation > 0
|
guard let donation, donation > 0
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
@ -99,9 +94,7 @@ struct EventProfileName: View {
|
|||||||
.frame(width: 14, height: 14)
|
.frame(width: 14, height: 14)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let supporter = self.supporter_percentage() {
|
SupporterBadge(percent: self.supporter_percentage(), purple_badge_info: self.purple_badge, style: .compact)
|
||||||
SupporterBadge(percent: supporter)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.profile_updated)) { update in
|
.onReceive(handle_notify(.profile_updated)) { update in
|
||||||
if update.pubkey != pubkey {
|
if update.pubkey != pubkey {
|
||||||
@ -129,7 +122,7 @@ struct EventProfileName: View {
|
|||||||
.onAppear(perform: {
|
.onAppear(perform: {
|
||||||
Task {
|
Task {
|
||||||
if damus_state.settings.enable_experimental_purple_api {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -41,12 +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?
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
var friend_type: FriendType? {
|
var friend_type: FriendType? {
|
||||||
@ -107,10 +109,15 @@ struct ProfileName: View {
|
|||||||
.frame(width: 14, height: 14)
|
.frame(width: 14, height: 14)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let supporter = supporter(profile: profile) {
|
SupporterBadge(percent: supporter(profile: profile), purple_badge_info: self.purple_badge, style: .full)
|
||||||
SupporterBadge(percent: supporter)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.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
|
.onReceive(handle_notify(.profile_updated)) { update in
|
||||||
if update.pubkey != pubkey {
|
if update.pubkey != pubkey {
|
||||||
return
|
return
|
||||||
|
@ -7,14 +7,6 @@
|
|||||||
|
|
||||||
import SwiftUI
|
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 {
|
struct SetupView: View {
|
||||||
@StateObject var navigationCoordinator: NavigationCoordinator = NavigationCoordinator()
|
@StateObject var navigationCoordinator: NavigationCoordinator = NavigationCoordinator()
|
||||||
|
Loading…
Reference in New Issue
Block a user