From ceecabe12c8c3ec53d282614fd919cd41e86a8c6 Mon Sep 17 00:00:00 2001 From: Marcus Kida Date: Tue, 1 Oct 2024 12:55:51 +0200 Subject: [PATCH] Begin implementing Donation Modal (IOS-252) --- Localization/app.json | 18 +++++ .../Donation/DonationViewController.swift | 78 ++++++++++++++++++- .../Generated/Strings.swift | 30 +++++++ .../Resources/Base.lproj/Localizable.strings | 12 ++- 4 files changed, 136 insertions(+), 2 deletions(-) diff --git a/Localization/app.json b/Localization/app.json index dc47417ef6..d43b481769 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -880,6 +880,10 @@ "show_followers_and_following": "Show Followers & Following", "suggest_my_account_to_others": "Suggest My Account to Others", "appear_in_search_engines": "Appear in Search Engines" + }, + "donation": { + "title": "Donate to Mastodon", + "manage": "Manage donations" } }, "report": { @@ -965,6 +969,20 @@ "follow": "Follow", "unfollow": "Unfollow" } + }, + "donation": { + "picker": { + "once_title": "Just once", + "monthly_title": "Monthly", + "yearly_title": "Yearly" + }, + "currency": "Currency", + "success": { + "title": "Thank you for your contribution!", + "subtitle": "You should receive an email confirming your donation soon.", + "server_error": "We are sorry, an error occurred and we have not been able to process your donation.\n\nPlease retry in a few minutes.", + "share_button_title": "Spread the word" + } } }, "extension": { diff --git a/Mastodon/Scene/Donation/DonationViewController.swift b/Mastodon/Scene/Donation/DonationViewController.swift index cc0e8c7b9f..3c7c318c77 100644 --- a/Mastodon/Scene/Donation/DonationViewController.swift +++ b/Mastodon/Scene/Donation/DonationViewController.swift @@ -4,10 +4,23 @@ import Foundation import SwiftUI import UIKit import MastodonSDK +import MastodonLocalization class DonationViewController: UIHostingController { init(campaign: Mastodon.Entity.DonationCampaign) { - super.init(rootView: DonationView(campaign: campaign)) + super.init( + rootView: DonationView( + campaign: campaign, + interval: campaign.amounts.monthly, + currency: campaign.amounts.monthly.first!.key, + amount: { + if let amount = campaign.amounts.monthly.first?.value.last { + return String(amount.asReadableAmount) + } + return "" + }() + ) + ) } @MainActor required dynamic init?(coder aDecoder: NSCoder) { @@ -18,9 +31,72 @@ class DonationViewController: UIHostingController { struct DonationView: View { let campaign: Mastodon.Entity.DonationCampaign + @State var interval: DonationAmount + @State var currency: String + @State var amount: String + var body: some View { VStack { Text(campaign.donationMessage) + .padding(.bottom, 16) + + Picker(selection: $interval) { + if let oneTime = campaign.amounts.oneTime { + Text(L10n.Scene.Donation.Picker.onceTitle) + .tag(oneTime) + } + Text(L10n.Scene.Donation.Picker.monthlyTitle) + .tag(campaign.amounts.monthly) + if let yearly = campaign.amounts.yearly { + Text(L10n.Scene.Donation.Picker.yearlyTitle) + .tag(yearly) + } + } label: {} + .pickerStyle(.segmented) + .padding(.bottom, 16) + + HStack { + Picker(selection: $currency) { + ForEach(interval.map(\.key), id: \.self) { + Text($0) + .tag($0) + } + } label: { + Text(currency) + } + .background(Color.gray.opacity(0.25)) + .clipShape(.rect(topLeadingRadius: 4, bottomLeadingRadius: 4)) + + TextField(text: $amount) {} + .keyboardType(.numberPad) + .multilineTextAlignment(.trailing) + .padding(.trailing, 8) + + } + .background(RoundedRectangle(cornerRadius: 4.0).stroke(Color.gray.opacity(0.25), lineWidth: 1)) + .padding(.bottom, 16) + + HStack { + if let predefinedAmounts = interval[currency] { + ForEach(predefinedAmounts, id: \.self) { amount in + Button(String(amount.asReadableAmount)) { + + } + .frame(minWidth: 100, maxWidth: .infinity) + .buttonStyle(.bordered) + } + } + } + .padding(.bottom, 16) + + } + .padding(.horizontal, 16) + } +} + +fileprivate extension Int { + var asReadableAmount: Int { + self / 100 } } } diff --git a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift index cea1404b15..c7c89b6cf5 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift +++ b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift @@ -808,6 +808,30 @@ public enum L10n { public static let posts = L10n.tr("Localizable", "Scene.Discovery.Tabs.Posts", fallback: "Posts") } } + public enum Donation { + /// Currency + public static let currency = L10n.tr("Localizable", "Scene.Donation.Currency", fallback: "Currency") + public enum Picker { + /// Monthly + public static let monthlyTitle = L10n.tr("Localizable", "Scene.Donation.Picker.MonthlyTitle", fallback: "Monthly") + /// Just once + public static let onceTitle = L10n.tr("Localizable", "Scene.Donation.Picker.OnceTitle", fallback: "Just once") + /// Yearly + public static let yearlyTitle = L10n.tr("Localizable", "Scene.Donation.Picker.YearlyTitle", fallback: "Yearly") + } + public enum Success { + /// We are sorry, an error occurred and we have not been able to process your donation. + /// + /// Please retry in a few minutes. + public static let serverError = L10n.tr("Localizable", "Scene.Donation.Success.ServerError", fallback: "We are sorry, an error occurred and we have not been able to process your donation.\n\nPlease retry in a few minutes.") + /// Spread the word + public static let shareButtonTitle = L10n.tr("Localizable", "Scene.Donation.Success.ShareButtonTitle", fallback: "Spread the word") + /// You should receive an email confirming your donation soon. + public static let subtitle = L10n.tr("Localizable", "Scene.Donation.Success.Subtitle", fallback: "You should receive an email confirming your donation soon.") + /// Thank you for your contribution! + public static let title = L10n.tr("Localizable", "Scene.Donation.Success.Title", fallback: "Thank you for your contribution!") + } + } public enum Familiarfollowers { /// Followed by %@ public static func followedByNames(_ p1: Any) -> String { @@ -1569,6 +1593,12 @@ public enum L10n { /// About public static let title = L10n.tr("Localizable", "Scene.Settings.AboutMastodon.Title", fallback: "About") } + public enum Donation { + /// Manage donations + public static let manage = L10n.tr("Localizable", "Scene.Settings.Donation.Manage", fallback: "Manage donations") + /// Donate to Mastodon + public static let title = L10n.tr("Localizable", "Scene.Settings.Donation.Title", fallback: "Donate to Mastodon") + } public enum General { /// General public static let title = L10n.tr("Localizable", "Scene.Settings.General.Title", fallback: "General") diff --git a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings index c8369ecf6a..e5be7f6692 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings +++ b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings @@ -299,6 +299,14 @@ uploaded to Mastodon."; "Scene.FollowedTags.Header.Posts" = "posts"; "Scene.FollowedTags.Header.PostsToday" = "posts today"; "Scene.FollowedTags.Title" = "Followed Tags"; +"Scene.Donation.Picker.OnceTitle" = "Just once"; +"Scene.Donation.Picker.MonthlyTitle" = "Monthly"; +"Scene.Donation.Picker.YearlyTitle" = "Yearly"; +"Scene.Donation.Currency" = "Currency"; +"Scene.Donation.Success.Title" = "Thank you for your contribution!"; +"Scene.Donation.Success.Subtitle" = "You should receive an email confirming your donation soon."; +"Scene.Donation.Success.ServerError" = "We are sorry, an error occurred and we have not been able to process your donation.\n\nPlease retry in a few minutes."; +"Scene.Donation.Success.ShareButtonTitle" = "Spread the word"; "Scene.Follower.Footer" = "Followers from other servers are not displayed."; "Scene.Follower.Title" = "follower"; "Scene.Following.Footer" = "Follows from other servers are not displayed."; @@ -599,6 +607,8 @@ If you disagree with the policy for **%@**, you can go back and pick a different "Scene.Settings.ServerDetails.AboutInstance.MessageAdmin" = "Message Admin"; "Scene.Settings.ServerDetails.AboutInstance.Title" = "Administrator"; "Scene.Settings.ServerDetails.Rules" = "Rules"; +"Scene.Settings.Donation.Title" = "Donate to Mastodon"; +"Scene.Settings.Donation.Manage" = "Manage donations"; "Scene.SuggestionAccount.FollowAll" = "Follow all"; "Scene.SuggestionAccount.Title" = "Popular on Mastodon"; "Scene.Thread.BackTitle" = "Post"; @@ -634,4 +644,4 @@ If you disagree with the policy for **%@**, you can go back and pick a different "Widget.MultipleFollowers.ConfigurationDescription" = "Show number of followers for multiple accounts."; "Widget.MultipleFollowers.ConfigurationDisplayName" = "Multiple followers"; "Widget.MultipleFollowers.MockUser.AccountName" = "another@follower.social"; -"Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower"; \ No newline at end of file +"Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower";