refactor: re-order items in DamusPurpleView
This is a purely non-functional refactor of DamusPurpleView consisting only of code mark section comments, and re-ordering for better organization and to facilitate further refactoring. Signed-off-by: Daniel D’Aquino <daniel@daquino.me> Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
committed by
William Casarin
parent
ab529c43eb
commit
1a131cd179
@@ -10,6 +10,8 @@ import StoreKit
|
|||||||
|
|
||||||
fileprivate let damus_products = ["purpleyearly","purple"]
|
fileprivate let damus_products = ["purpleyearly","purple"]
|
||||||
|
|
||||||
|
// MARK: - Helper structures
|
||||||
|
|
||||||
enum ProductState {
|
enum ProductState {
|
||||||
case loading
|
case loading
|
||||||
case loaded([Product])
|
case loaded([Product])
|
||||||
@@ -48,6 +50,8 @@ struct PurchasedProduct {
|
|||||||
let product: Product
|
let product: Product
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Main view
|
||||||
|
|
||||||
struct DamusPurpleView: View {
|
struct DamusPurpleView: View {
|
||||||
let damus_state: DamusState
|
let damus_state: DamusState
|
||||||
let keypair: Keypair
|
let keypair: Keypair
|
||||||
@@ -68,6 +72,8 @@ struct DamusPurpleView: View {
|
|||||||
self.keypair = damus_state.keypair
|
self.keypair = damus_state.keypair
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Top level view
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
ZStack {
|
ZStack {
|
||||||
@@ -115,238 +121,7 @@ struct DamusPurpleView: View {
|
|||||||
.manageSubscriptionsSheet(isPresented: $show_manage_subscriptions)
|
.manageSubscriptionsSheet(isPresented: $show_manage_subscriptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
func load_account() async {
|
// MARK: - Complex subviews
|
||||||
do {
|
|
||||||
if let account = try await damus_state.purple.fetch_account(pubkey: damus_state.keypair.pubkey) {
|
|
||||||
self.my_account_info_state = .loaded(account: account)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.my_account_info_state = .no_account
|
|
||||||
return
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
self.my_account_info_state = .error(message: NSLocalizedString("There was an error loading your account. Please try again later. If problem persists, please contact us at support@damus.io", comment: "Error label when Purple account information fails to load"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handle_transactions(products: [Product]) async {
|
|
||||||
for await update in StoreKit.Transaction.updates {
|
|
||||||
switch update {
|
|
||||||
case .verified(let tx):
|
|
||||||
let prod = products.filter({ prod in tx.productID == prod.id }).first
|
|
||||||
|
|
||||||
if let prod,
|
|
||||||
let expiration = tx.expirationDate,
|
|
||||||
Date.now < expiration
|
|
||||||
{
|
|
||||||
self.purchased = PurchasedProduct(tx: tx, product: prod)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case .unverified:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func load_products() async {
|
|
||||||
do {
|
|
||||||
let products = try await Product.products(for: damus_products)
|
|
||||||
self.products = .loaded(products)
|
|
||||||
await handle_transactions(products: products)
|
|
||||||
|
|
||||||
print("loaded products", products)
|
|
||||||
} catch {
|
|
||||||
self.products = .failed
|
|
||||||
print("Failed to fetch products: \(error.localizedDescription)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func IconOnBox(_ name: String) -> some View {
|
|
||||||
ZStack {
|
|
||||||
RoundedRectangle(cornerRadius: 20.0)
|
|
||||||
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 20.0))
|
|
||||||
.frame(width: 80, height: 80)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 20)
|
|
||||||
.stroke(LinearGradient(
|
|
||||||
colors: [DamusColors.pink, .white.opacity(0), .white.opacity(0.5), .white.opacity(0)],
|
|
||||||
startPoint: .topLeading,
|
|
||||||
endPoint: .bottomTrailing), lineWidth: 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
Image(name)
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 50, height: 50)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Icon(_ name: String) -> some View {
|
|
||||||
Image(name)
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 50, height: 50)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Title(_ txt: String) -> some View {
|
|
||||||
Text(txt)
|
|
||||||
.font(.title3)
|
|
||||||
.bold()
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.padding(.bottom, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Subtitle(_ txt: String) -> some View {
|
|
||||||
Text(txt)
|
|
||||||
.foregroundColor(.white.opacity(0.65))
|
|
||||||
}
|
|
||||||
|
|
||||||
var ProductLoadError: some View {
|
|
||||||
Text(NSLocalizedString("Subscription Error", comment: "Ah dang there was an error loading subscription information from the AppStore. Please try again later :("))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
}
|
|
||||||
|
|
||||||
var SaveText: Text {
|
|
||||||
Text(NSLocalizedString("Save 14%", comment: "Percentage of purchase price the user will save"))
|
|
||||||
.font(.callout)
|
|
||||||
.italic()
|
|
||||||
.foregroundColor(DamusColors.green)
|
|
||||||
}
|
|
||||||
|
|
||||||
func subscribe(_ product: Product) async throws {
|
|
||||||
let result = try await product.purchase()
|
|
||||||
switch result {
|
|
||||||
case .success(.verified(let tx)):
|
|
||||||
print("success \(tx.debugDescription)")
|
|
||||||
show_welcome_sheet = true
|
|
||||||
case .success(.unverified(let tx, let res)):
|
|
||||||
print("success unverified \(tx.debugDescription) \(res.localizedDescription)")
|
|
||||||
show_welcome_sheet = true
|
|
||||||
case .pending:
|
|
||||||
break
|
|
||||||
case .userCancelled:
|
|
||||||
break
|
|
||||||
@unknown default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
switch result {
|
|
||||||
case .success:
|
|
||||||
// TODO (will): why do this here?
|
|
||||||
//self.damus_state.purple.starred_profiles_cache[keypair.pubkey] = nil
|
|
||||||
Task {
|
|
||||||
await self.damus_state.purple.send_receipt()
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var product: Product? {
|
|
||||||
return self.products.products?.filter({
|
|
||||||
prod in prod.id == selection.rawValue
|
|
||||||
}).first
|
|
||||||
}
|
|
||||||
|
|
||||||
func price_description(product: Product) -> some View {
|
|
||||||
if product.id == "purpleyearly" {
|
|
||||||
return (
|
|
||||||
AnyView(
|
|
||||||
HStack(spacing: 10) {
|
|
||||||
Text(NSLocalizedString("Annually", comment: "Annual renewal of purple subscription"))
|
|
||||||
Spacer()
|
|
||||||
Text(verbatim: non_discounted_price(product)).strikethrough().foregroundColor(DamusColors.white.opacity(0.5))
|
|
||||||
Text(verbatim: product.displayPrice).fontWeight(.bold)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
AnyView(
|
|
||||||
HStack(spacing: 10) {
|
|
||||||
Text(NSLocalizedString("Monthly", comment: "Monthly renewal of purple subscription"))
|
|
||||||
Spacer()
|
|
||||||
Text(verbatim: product.displayPrice).fontWeight(.bold)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProductsView(_ products: [Product]) -> some View {
|
|
||||||
VStack(spacing: 10) {
|
|
||||||
Text(NSLocalizedString("Save 20% off on an annual subscription", comment: "Savings for purchasing an annual subscription"))
|
|
||||||
.font(.callout.bold())
|
|
||||||
.foregroundColor(.white)
|
|
||||||
ForEach(products) { product in
|
|
||||||
Button(action: {
|
|
||||||
Task { @MainActor in
|
|
||||||
do {
|
|
||||||
try await subscribe(product)
|
|
||||||
} catch {
|
|
||||||
print(error.localizedDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, label: {
|
|
||||||
price_description(product: product)
|
|
||||||
})
|
|
||||||
.buttonStyle(GradientButtonStyle())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
|
||||||
|
|
||||||
func PurchasedView(_ purchased: PurchasedProduct) -> some View {
|
|
||||||
VStack(spacing: 10) {
|
|
||||||
Text(NSLocalizedString("Purchased!", comment: "User purchased a subscription"))
|
|
||||||
.font(.title2)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
price_description(product: purchased.product)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.opacity(0.65)
|
|
||||||
.frame(width: 200)
|
|
||||||
Text(NSLocalizedString("Purchased on", comment: "Indicating when the user purchased the subscription"))
|
|
||||||
.font(.title2)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
Text(format_date(date: purchased.tx.purchaseDate))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.opacity(0.65)
|
|
||||||
if let expiry = purchased.tx.expirationDate {
|
|
||||||
Text(NSLocalizedString("Renews on", comment: "Indicating when the subscription will renew"))
|
|
||||||
.font(.title2)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
Text(format_date(date: expiry))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.opacity(0.65)
|
|
||||||
}
|
|
||||||
Button(action: {
|
|
||||||
show_manage_subscriptions = true
|
|
||||||
}, label: {
|
|
||||||
Text(NSLocalizedString("Manage", comment: "Manage the damus subscription"))
|
|
||||||
})
|
|
||||||
.buttonStyle(GradientButtonStyle())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ProductStateView: some View {
|
|
||||||
Group {
|
|
||||||
if damus_state.purple.enable_purple_iap_support {
|
|
||||||
switch self.products {
|
|
||||||
case .failed:
|
|
||||||
ProductLoadError
|
|
||||||
case .loaded(let products):
|
|
||||||
if let purchased {
|
|
||||||
PurchasedView(purchased)
|
|
||||||
} else {
|
|
||||||
ProductsView(products)
|
|
||||||
}
|
|
||||||
case .loading:
|
|
||||||
ProgressView()
|
|
||||||
.progressViewStyle(.circular)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var MainContent: some View {
|
var MainContent: some View {
|
||||||
VStack {
|
VStack {
|
||||||
@@ -448,8 +223,247 @@ struct DamusPurpleView: View {
|
|||||||
.padding([.top], 20)
|
.padding([.top], 20)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ProductStateView: some View {
|
||||||
|
Group {
|
||||||
|
if damus_state.purple.enable_purple_iap_support {
|
||||||
|
switch self.products {
|
||||||
|
case .failed:
|
||||||
|
ProductLoadError
|
||||||
|
case .loaded(let products):
|
||||||
|
if let purchased {
|
||||||
|
PurchasedView(purchased)
|
||||||
|
} else {
|
||||||
|
ProductsView(products)
|
||||||
|
}
|
||||||
|
case .loading:
|
||||||
|
ProgressView()
|
||||||
|
.progressViewStyle(.circular)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PurchasedView(_ purchased: PurchasedProduct) -> some View {
|
||||||
|
VStack(spacing: 10) {
|
||||||
|
Text(NSLocalizedString("Purchased!", comment: "User purchased a subscription"))
|
||||||
|
.font(.title2)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
price_description(product: purchased.product)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.opacity(0.65)
|
||||||
|
.frame(width: 200)
|
||||||
|
Text(NSLocalizedString("Purchased on", comment: "Indicating when the user purchased the subscription"))
|
||||||
|
.font(.title2)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
Text(format_date(date: purchased.tx.purchaseDate))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.opacity(0.65)
|
||||||
|
if let expiry = purchased.tx.expirationDate {
|
||||||
|
Text(NSLocalizedString("Renews on", comment: "Indicating when the subscription will renew"))
|
||||||
|
.font(.title2)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
Text(format_date(date: expiry))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.opacity(0.65)
|
||||||
|
}
|
||||||
|
Button(action: {
|
||||||
|
show_manage_subscriptions = true
|
||||||
|
}, label: {
|
||||||
|
Text(NSLocalizedString("Manage", comment: "Manage the damus subscription"))
|
||||||
|
})
|
||||||
|
.buttonStyle(GradientButtonStyle())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProductsView(_ products: [Product]) -> some View {
|
||||||
|
VStack(spacing: 10) {
|
||||||
|
Text(NSLocalizedString("Save 20% off on an annual subscription", comment: "Savings for purchasing an annual subscription"))
|
||||||
|
.font(.callout.bold())
|
||||||
|
.foregroundColor(.white)
|
||||||
|
ForEach(products) { product in
|
||||||
|
Button(action: {
|
||||||
|
Task { @MainActor in
|
||||||
|
do {
|
||||||
|
try await subscribe(product)
|
||||||
|
} catch {
|
||||||
|
print(error.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, label: {
|
||||||
|
price_description(product: product)
|
||||||
|
})
|
||||||
|
.buttonStyle(GradientButtonStyle())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
func price_description(product: Product) -> some View {
|
||||||
|
if product.id == "purpleyearly" {
|
||||||
|
return (
|
||||||
|
AnyView(
|
||||||
|
HStack(spacing: 10) {
|
||||||
|
Text(NSLocalizedString("Annually", comment: "Annual renewal of purple subscription"))
|
||||||
|
Spacer()
|
||||||
|
Text(verbatim: non_discounted_price(product)).strikethrough().foregroundColor(DamusColors.white.opacity(0.5))
|
||||||
|
Text(verbatim: product.displayPrice).fontWeight(.bold)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
AnyView(
|
||||||
|
HStack(spacing: 10) {
|
||||||
|
Text(NSLocalizedString("Monthly", comment: "Monthly renewal of purple subscription"))
|
||||||
|
Spacer()
|
||||||
|
Text(verbatim: product.displayPrice).fontWeight(.bold)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - State management
|
||||||
|
|
||||||
|
func load_account() async {
|
||||||
|
do {
|
||||||
|
if let account = try await damus_state.purple.fetch_account(pubkey: damus_state.keypair.pubkey) {
|
||||||
|
self.my_account_info_state = .loaded(account: account)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.my_account_info_state = .no_account
|
||||||
|
return
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
self.my_account_info_state = .error(message: NSLocalizedString("There was an error loading your account. Please try again later. If problem persists, please contact us at support@damus.io", comment: "Error label when Purple account information fails to load"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle_transactions(products: [Product]) async {
|
||||||
|
for await update in StoreKit.Transaction.updates {
|
||||||
|
switch update {
|
||||||
|
case .verified(let tx):
|
||||||
|
let prod = products.filter({ prod in tx.productID == prod.id }).first
|
||||||
|
|
||||||
|
if let prod,
|
||||||
|
let expiration = tx.expirationDate,
|
||||||
|
Date.now < expiration
|
||||||
|
{
|
||||||
|
self.purchased = PurchasedProduct(tx: tx, product: prod)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case .unverified:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func load_products() async {
|
||||||
|
do {
|
||||||
|
let products = try await Product.products(for: damus_products)
|
||||||
|
self.products = .loaded(products)
|
||||||
|
await handle_transactions(products: products)
|
||||||
|
|
||||||
|
print("loaded products", products)
|
||||||
|
} catch {
|
||||||
|
self.products = .failed
|
||||||
|
print("Failed to fetch products: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func subscribe(_ product: Product) async throws {
|
||||||
|
let result = try await product.purchase()
|
||||||
|
switch result {
|
||||||
|
case .success(.verified(let tx)):
|
||||||
|
print("success \(tx.debugDescription)")
|
||||||
|
show_welcome_sheet = true
|
||||||
|
case .success(.unverified(let tx, let res)):
|
||||||
|
print("success unverified \(tx.debugDescription) \(res.localizedDescription)")
|
||||||
|
show_welcome_sheet = true
|
||||||
|
case .pending:
|
||||||
|
break
|
||||||
|
case .userCancelled:
|
||||||
|
break
|
||||||
|
@unknown default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch result {
|
||||||
|
case .success:
|
||||||
|
// TODO (will): why do this here?
|
||||||
|
//self.damus_state.purple.starred_profiles_cache[keypair.pubkey] = nil
|
||||||
|
Task {
|
||||||
|
await self.damus_state.purple.send_receipt()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var product: Product? {
|
||||||
|
return self.products.products?.filter({
|
||||||
|
prod in prod.id == selection.rawValue
|
||||||
|
}).first
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Small helper views
|
||||||
|
|
||||||
|
func IconOnBox(_ name: String) -> some View {
|
||||||
|
ZStack {
|
||||||
|
RoundedRectangle(cornerRadius: 20.0)
|
||||||
|
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 20.0))
|
||||||
|
.frame(width: 80, height: 80)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 20)
|
||||||
|
.stroke(LinearGradient(
|
||||||
|
colors: [DamusColors.pink, .white.opacity(0), .white.opacity(0.5), .white.opacity(0)],
|
||||||
|
startPoint: .topLeading,
|
||||||
|
endPoint: .bottomTrailing), lineWidth: 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
Image(name)
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 50, height: 50)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Icon(_ name: String) -> some View {
|
||||||
|
Image(name)
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 50, height: 50)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Title(_ txt: String) -> some View {
|
||||||
|
Text(txt)
|
||||||
|
.font(.title3)
|
||||||
|
.bold()
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.padding(.bottom, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Subtitle(_ txt: String) -> some View {
|
||||||
|
Text(txt)
|
||||||
|
.foregroundColor(.white.opacity(0.65))
|
||||||
|
}
|
||||||
|
|
||||||
|
var ProductLoadError: some View {
|
||||||
|
Text(NSLocalizedString("Subscription Error", comment: "Ah dang there was an error loading subscription information from the AppStore. Please try again later :("))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
}
|
||||||
|
|
||||||
|
var SaveText: Text {
|
||||||
|
Text(NSLocalizedString("Save 14%", comment: "Percentage of purchase price the user will save"))
|
||||||
|
.font(.callout)
|
||||||
|
.italic()
|
||||||
|
.foregroundColor(DamusColors.green)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - More helper views
|
||||||
|
|
||||||
struct DamusPurpleLogoView: View {
|
struct DamusPurpleLogoView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 20) {
|
HStack(spacing: 20) {
|
||||||
|
|||||||
Reference in New Issue
Block a user