diff --git a/Tusker/Screens/Preferences/Tip Jar/TipJarView.swift b/Tusker/Screens/Preferences/Tip Jar/TipJarView.swift index 6825cc54..1f9482e9 100644 --- a/Tusker/Screens/Preferences/Tip Jar/TipJarView.swift +++ b/Tusker/Screens/Preferences/Tip Jar/TipJarView.swift @@ -32,12 +32,12 @@ struct TipJarView: View { @StateObject private var observer = UbiquitousKeyValueStoreObserver() var body: some View { - ZStack { - Color.appGroupedBackground - .edgesIgnoringSafeArea(.all) - - productsView - + List { + productsOrLoading + } + .listStyle(.insetGrouped) + .appGroupedListBackground(container: PreferencesNavigationController.self) + .overlay { if showConfetti { ConfettiView() .transition(.opacity.animation(.default)) @@ -75,46 +75,66 @@ struct TipJarView: View { .onDisappear { updatesObserver?.cancel() } - .onReceive(Just(showConfetti).filter { $0 }.delay(for: .seconds(5), scheduler: DispatchQueue.main)) { _ in - showConfetti = false + .onChange(of: showConfetti) { newValue in + if newValue { + Task { + try await Task.sleep(nanoseconds: 5 * NSEC_PER_SEC) + showConfetti = false + } + } } } @ViewBuilder - private var productsView: some View { + private var productsOrLoading: some View { if isLoaded { - VStack { - if !supporterProducts.isEmpty { - supporterSubscriptions - } - - tipPurchases - - if let tipStatus { - tipStatus - .multilineTextAlignment(.center) - .padding(.horizontal) - .padding(.top, 16) - Text("Thank you!") - } - } + productsSections } else { ProgressView() .progressViewStyle(.circular) + .frame(maxWidth: .infinity, alignment: .center) + .listRowBackground(Color.clear) } } @ViewBuilder - private var supporterSubscriptions: some View { - Text("If you want to contribute Tusker's continued development, you can become a supporter. Supporting Tusker is an auto-renewable monthly subscription.") - .multilineTextAlignment(.center) - .padding(.horizontal) + private var productsSections: some View { + if let tipStatus { + Section { + VStack(alignment: .leading) { + tipStatus + .multilineTextAlignment(.leading) + Text("Thank you!") + } + } + .appGroupedListRowBackground() + } - VStack(alignment: .myAlignment) { + if !supporterProducts.isEmpty { + supporterSection + } + + tipSection + } + + private var supporterSection: some View { + Section { + Text("If you want to contribute Tusker's continued development, you can become a supporter and give a montly tip.") + .multilineTextAlignment(.leading) + ForEach($supporterProducts, id: \.0.id) { $productAndPurchasing in TipRow(product: productAndPurchasing.0, buttonWidth: supporterButtonWidth, isPurchasing: $productAndPurchasing.1, showConfetti: $showConfetti) } + } footer: { + VStack { + var privacyPolicy: AttributedString = "Privacy Policy" + let _ = privacyPolicy.link = URL(string: "https://vaccor.space/tusker#privacy")! + var eula: AttributedString = "EULA" + let _ = eula.link = URL(string: "https://www.apple.com/legal/internet-services/itunes/dev/stdeula/")! + Text(AttributedString("Supporting Tusker is an auto-renewable monthly subscription. Subscribing does not provide additional features or capabilities.\n") + privacyPolicy + AttributedString(" and ") + eula) + } } + .appGroupedListRowBackground() .onPreferenceChange(ButtonWidthKey.self) { newValue in if let supporterButtonWidth { self.supporterButtonWidth = max(supporterButtonWidth, newValue) @@ -124,18 +144,16 @@ struct TipJarView: View { } } - @ViewBuilder - private var tipPurchases: some View { - Text("Or, you can choose to make a one-time tip to show your gratitutde or help support the app's development. It is greatly appreciated!") - .multilineTextAlignment(.center) - .padding(.horizontal) - .padding(.top, 16) + private var tipSection: some View { + Section { + Text("Or, you can choose to make a one-time tip to show your gratitutde or help support the app's development. It is greatly appreciated!") + .multilineTextAlignment(.leading) - VStack(alignment: .myAlignment) { ForEach($tipProducts, id: \.0.id) { $productAndPurchasing in TipRow(product: productAndPurchasing.0, buttonWidth: tipButtonWidth, isPurchasing: $productAndPurchasing.1, showConfetti: $showConfetti) } } + .appGroupedListRowBackground() .onPreferenceChange(ButtonWidthKey.self) { newValue in if let tipButtonWidth { self.tipButtonWidth = max(tipButtonWidth, newValue) @@ -231,6 +249,8 @@ private struct TipRow: View { Text(product.displayName) .alignmentGuide(.myAlignment, computeValue: { context in context[.trailing] }) + Spacer() + if let subscription = product.subscription { SubscriptionButton(product: product, subscriptionInfo: subscription, isPurchasing: $isPurchasing, buttonWidth: buttonWidth, purchase: purchase) } else {