Paywall improvements

This commit is contained in:
Kylmakalle 2025-02-25 01:46:09 +02:00
parent 34cc1ba5b3
commit d9665f5968
8 changed files with 103 additions and 19 deletions

View File

@ -152,7 +152,6 @@ public func sgDebugController(context: AccountContext) -> ViewController {
}
#endif
case .resetIAP:
#if DEBUG
let updateSettingsSignal = updateSGStatusInteractively(accountManager: context.sharedContext.accountManager, { status in
var status = status
status.status = SGStatus.default.status
@ -168,7 +167,6 @@ public func sgDebugController(context: AccountContext) -> ViewController {
),
nil)
})
#endif
}
})

View File

@ -13,7 +13,7 @@ import TelegramUIPreferences
@available(iOS 13.0, *)
public func sgPayWallController(statusSignal: Signal<Int64, NoError>, replacementController: ViewController, presentationData: PresentationData? = nil, SGIAPManager: SGIAPManager, openUrl: @escaping (String) -> Void) -> ViewController {
public func sgPayWallController(statusSignal: Signal<Int64, NoError>, replacementController: ViewController, presentationData: PresentationData? = nil, SGIAPManager: SGIAPManager, openUrl: @escaping (String) -> Void, paymentsEnabled: Bool) -> ViewController {
// let theme = presentationData?.theme ?? (UITraitCollection.current.userInterfaceStyle == .dark ? defaultDarkColorPresentationTheme : defaultPresentationTheme)
let theme = defaultDarkColorPresentationTheme
let strings = presentationData?.strings ?? defaultPresentationStrings
@ -30,7 +30,7 @@ public func sgPayWallController(statusSignal: Signal<Int64, NoError>, replacemen
let swiftUIView = SGSwiftUIView<SGPayWallView>(
legacyController: legacyController,
content: {
SGPayWallView(wrapperController: legacyController, replacementController: replacementController, SGIAP: SGIAPManager, statusSignal: statusSignal, openUrl: openUrl)
SGPayWallView(wrapperController: legacyController, replacementController: replacementController, SGIAP: SGIAPManager, statusSignal: statusSignal, openUrl: openUrl, paymentsEnabled: paymentsEnabled)
}
)
let controller = UIHostingController(rootView: swiftUIView, ignoreSafeArea: true)
@ -106,6 +106,7 @@ struct SGPayWallView: View {
let SGIAP: SGIAPManager
let statusSignal: Signal<Int64, NoError>
let openUrl: (String) -> Void
let paymentsEnabled: Bool
private enum PayWallState: Equatable {
case ready // ready to buy
@ -155,11 +156,23 @@ struct SGPayWallView: View {
}
// Features
VStack(spacing: 8) {
VStack(spacing: 36) {
featuresSection
legalSection
restorePurchasesButton
}
aboutSection
VStack(spacing: 8) {
HStack {
legalSection
Spacer()
}
HStack {
restorePurchasesButton
Spacer()
}
}
}
// Spacer for purchase buttons
@ -256,6 +269,14 @@ struct SGPayWallView: View {
title: "PayWall.InputToolbar.Title".i18n(lang),
subtitle: "PayWall.InputToolbar.Notice".i18n(lang)
)
FeatureRow(
icon: Image("SwiftgramSettings")
.resizable()
.frame(width: 32, height: 32),
title: "PayWall.AppIcons.Title".i18n(lang),
subtitle: "PayWall.AppIcons.Notice".i18n(lang)
)
}
}
@ -328,6 +349,25 @@ struct SGPayWallView: View {
}
}
private var aboutSection: some View {
VStack(spacing: 8) {
HStack {
Text("PayWall.About.Title".i18n(lang))
.font(.headline)
.fontWeight(.medium)
Spacer()
}
HStack {
Text("PayWall.About.Notice".i18n(lang))
.font(.subheadline)
.foregroundColor(.secondary)
Spacer()
}
}
}
private var closeButtonView: some View {
Button(action: {
wrapperController?.dismiss(animated: true)
@ -353,7 +393,7 @@ struct SGPayWallView: View {
} else if state == .validating {
return "PayWall.Button.Validating".i18n(lang)
} else if let product = product {
if !SGIAP.canMakePayments {
if !SGIAP.canMakePayments || paymentsEnabled == false {
return "PayWall.Button.PaymentsUnavailable".i18n(lang)
} else {
return "PayWall.Button.Subscribe".i18n(lang, args: product.price)
@ -365,7 +405,7 @@ struct SGPayWallView: View {
}
private var canPurchase: Bool {
if !SGIAP.canMakePayments {
if !SGIAP.canMakePayments || paymentsEnabled == false {
return false
} else {
return product != nil
@ -453,8 +493,8 @@ struct FeatureIcon: View {
@available(iOS 13.0, *)
struct FeatureRow: View {
let icon: FeatureIcon
struct FeatureRow<IconContent: View>: View {
let icon: IconContent
let title: String
let subtitle: String

View File

@ -20,6 +20,7 @@ import SGLogging
private enum SGProControllerSection: Int32, SGItemListSection {
case base
case notifications
case footer
}
private enum SGProDisclosureLink: String {
@ -36,7 +37,11 @@ private enum SGProOneFromManySetting: String {
case mentionsAndRepliesNotifications
}
private typealias SGProControllerEntry = SGItemListUIEntry<SGProControllerSection, SGProToggles, AnyHashable, SGProOneFromManySetting, SGProDisclosureLink, AnyHashable>
private enum SGProAction {
case resetIAP
}
private typealias SGProControllerEntry = SGItemListUIEntry<SGProControllerSection, SGProToggles, AnyHashable, SGProOneFromManySetting, SGProDisclosureLink, SGProAction>
private func SGProControllerEntries(presentationData: PresentationData) -> [SGProControllerEntry] {
var entries: [SGProControllerEntry] = []
@ -52,6 +57,10 @@ private func SGProControllerEntries(presentationData: PresentationData) -> [SGPr
entries.append(.oneFromManySelector(id: id.count, section: .notifications, settingName: .pinnedMessageNotifications, text: "Notifications.PinnedMessages.Title".i18n(lang), value: "Notifications.PinnedMessages.value.\(SGSimpleSettings.shared.pinnedMessageNotifications)".i18n(lang), enabled: true))
entries.append(.oneFromManySelector(id: id.count, section: .notifications, settingName: .mentionsAndRepliesNotifications, text: "Notifications.MentionsAndReplies.Title".i18n(lang), value: "Notifications.MentionsAndReplies.value.\(SGSimpleSettings.shared.mentionsAndRepliesNotifications)".i18n(lang), enabled: true))
#if DEBUG
entries.append(.action(id: id.count, section: .footer, actionType: .resetIAP, text: "Reset Pro", kind: .destructive))
#endif
return entries
}
@ -65,7 +74,7 @@ public func sgProController(context: AccountContext) -> ViewController {
let simplePromise = ValuePromise(true, ignoreRepeated: false)
let arguments = SGItemListArguments<SGProToggles, AnyHashable, SGProOneFromManySetting, SGProDisclosureLink, AnyHashable>(context: context, setBoolValue: { toggleName, value in
let arguments = SGItemListArguments<SGProToggles, AnyHashable, SGProOneFromManySetting, SGProDisclosureLink, SGProAction>(context: context, setBoolValue: { toggleName, value in
switch toggleName {
case .inputToolbar:
SGSimpleSettings.shared.inputToolbar = value
@ -127,8 +136,26 @@ public func sgProController(context: AccountContext) -> ViewController {
presentControllerImpl?(context.sharedContext.makeSGUpdateIOSController(), nil)
}
}
}, action: { _ in
}, action: { action in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
switch action {
case .resetIAP:
let updateSettingsSignal = updateSGStatusInteractively(accountManager: context.sharedContext.accountManager, { status in
var status = status
status.status = SGStatus.default.status
SGSimpleSettings.shared.primaryUserId = ""
return status
})
let _ = (updateSettingsSignal |> deliverOnMainQueue).start(next: {
presentControllerImpl?(UndoOverlayController(
presentationData: presentationData,
content: .info(title: nil, text: "Status reset completed. You can now restore purchases.", timeout: nil, customUndoText: nil),
elevatedLayout: false,
action: { _ in return false }
),
nil)
})
}
})
let signal = combineLatest(context.sharedContext.presentationData, simplePromise.get())

View File

@ -245,7 +245,7 @@ struct SessionBackupManagerView: View {
private func performDelete(_ session: SessionBackup) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = textAlertController(context: context, title: "SessionBackup.DeleteSingle.Title".i18n(lang), text: "Sessionbackup.DeleteSingle.Text".i18n(lang, args: "\(session.name ?? "\(session.userId)")"), actions: [
let controller = textAlertController(context: context, title: "SessionBackup.DeleteSingle.Title".i18n(lang), text: "SessionBackup.DeleteSingle.Text".i18n(lang, args: "\(session.name ?? "\(session.userId)")"), actions: [
TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: {
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
wrapperController?.present(controller, in: .window(.root), with: nil)

View File

@ -203,6 +203,12 @@
"PayWall.InputToolbar.Title" = "Formatting Panel";
"PayWall.InputToolbar.Notice" = "Save time preparing your posts with a panel right above your keyboard.";
"PayWall.AppIcons.Title" = "Unique App Icons";
"PayWall.AppIcons.Notice" = "Customize Swiftgram look on your home screen.";
"PayWall.About.Title" = "About Swiftgram Pro";
"PayWall.About.Notice" = "Free version of Swiftgram provides dozens of features and improvements over Telegram app. Innovating and keeping Swiftgram in sync with monthly Telegram updates is a huge effort that requires a lot of time and latest hardware.\n\nSwiftgram is an open-source app that respects your privacy and doesn't bother you with ads. Subscribing to Swiftgram Pro you get access to exclusive features and support an independent developer.\n\n- @Kylmakalle";
"PayWall.RestorePurchases" = "Restore Purchases";
"PayWall.Terms" = "Terms of Service";
"PayWall.Privacy" = "Privacy Policy";

View File

@ -5,7 +5,7 @@ public struct SGWebSettings: Codable, Equatable {
public let user: SGUserSettings
public static var defaultValue: SGWebSettings {
return SGWebSettings(global: SGGlobalSettings(ytPip: true, qrLogin: true, storiesAvailable: false, canViewMessages: true, canEditSettings: false, canShowTelescope: false, announcementsData: nil, regdateFormat: "full", botMonkeys: [], forceReasons: [], unforceReasons: []), user: SGUserSettings(contentReasons: [], canSendTelescope: false))
return SGWebSettings(global: SGGlobalSettings(ytPip: true, qrLogin: true, storiesAvailable: false, canViewMessages: true, canEditSettings: false, canShowTelescope: false, announcementsData: nil, regdateFormat: "full", botMonkeys: [], forceReasons: [], unforceReasons: [], paymentsEnabled: true), user: SGUserSettings(contentReasons: [], canSendTelescope: false))
}
}
@ -21,6 +21,7 @@ public struct SGGlobalSettings: Codable, Equatable {
public let botMonkeys: [SGBotMonkeys]
public let forceReasons: [Int64]
public let unforceReasons: [Int64]
public let paymentsEnabled: Bool
}
public struct SGBotMonkeys: Codable, Equatable {

View File

@ -1013,6 +1013,18 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
lastViewController.present(ContactsController(context: context), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}
return
case "pro", "premium", "buy":
if context.sharedContext.immediateSGStatus.status > 1 {
navigationController?.pushViewController(context.sharedContext.makeSGProController(context: context))
} else {
if let lastViewController = navigationController?.viewControllers.last as? ViewController {
if let payWallController = context.sharedContext.makeSGPayWallController(context: context) {
lastViewController.present(payWallController, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
} else {
lastViewController.present(context.sharedContext.makeSGUpdateIOSController(), animated: true)
}
}
}
default:
break
}

View File

@ -3077,7 +3077,7 @@ extension SharedAccountContextImpl {
}
let proController = self.makeSGProController(context: context)
let payWallController = sgPayWallController(statusSignal: statusSignal, replacementController: proController, presentationData: self.currentPresentationData.with { $0 }, SGIAPManager: sgIAP, openUrl: self.applicationBindings.openUrl)
let payWallController = sgPayWallController(statusSignal: statusSignal, replacementController: proController, presentationData: self.currentPresentationData.with { $0 }, SGIAPManager: sgIAP, openUrl: self.applicationBindings.openUrl, paymentsEnabled: context.currentAppConfiguration.with { $0 }.sgWebSettings.global.paymentsEnabled)
return payWallController
}