mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Pro feature details
This commit is contained in:
parent
f21ebab517
commit
74ca4ab8a0
@ -159,17 +159,14 @@ public func sgDebugController(context: AccountContext) -> ViewController {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
case .restorePurchases:
|
case .restorePurchases:
|
||||||
context.sharedContext.SGIAP?.restorePurchases {
|
presentControllerImpl?(UndoOverlayController(
|
||||||
DispatchQueue.main.async {
|
presentationData: presentationData,
|
||||||
presentControllerImpl?(UndoOverlayController(
|
content: .info(title: nil, text: "PayWall.Button.Restoring".i18n(args: context.sharedContext.currentPresentationData.with { $0 }.strings.baseLanguageCode), timeout: nil, customUndoText: nil),
|
||||||
presentationData: presentationData,
|
elevatedLayout: false,
|
||||||
content: .info(title: nil, text: "PayWall.Button.Restoring".i18n(args: context.sharedContext.currentPresentationData.with { $0 }.strings.baseLanguageCode), timeout: nil, customUndoText: nil),
|
action: { _ in return false }
|
||||||
elevatedLayout: false,
|
),
|
||||||
action: { _ in return false }
|
nil)
|
||||||
),
|
context.sharedContext.SGIAP?.restorePurchases {}
|
||||||
nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case .setIAP:
|
case .setIAP:
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
BIN
Swiftgram/SGPayWall/Images.xcassets/ProDetailsBackup.imageset/Backup.png
vendored
Normal file
BIN
Swiftgram/SGPayWall/Images.xcassets/ProDetailsBackup.imageset/Backup.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 KiB |
21
Swiftgram/SGPayWall/Images.xcassets/ProDetailsBackup.imageset/Contents.json
vendored
Normal file
21
Swiftgram/SGPayWall/Images.xcassets/ProDetailsBackup.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Backup.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
21
Swiftgram/SGPayWall/Images.xcassets/ProDetailsFilter.imageset/Contents.json
vendored
Normal file
21
Swiftgram/SGPayWall/Images.xcassets/ProDetailsFilter.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Filter.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
Swiftgram/SGPayWall/Images.xcassets/ProDetailsFilter.imageset/Filter.png
vendored
Normal file
BIN
Swiftgram/SGPayWall/Images.xcassets/ProDetailsFilter.imageset/Filter.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 522 KiB |
21
Swiftgram/SGPayWall/Images.xcassets/ProDetailsFormatting.imageset/Contents.json
vendored
Normal file
21
Swiftgram/SGPayWall/Images.xcassets/ProDetailsFormatting.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Formatting.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
Swiftgram/SGPayWall/Images.xcassets/ProDetailsFormatting.imageset/Formatting.png
vendored
Normal file
BIN
Swiftgram/SGPayWall/Images.xcassets/ProDetailsFormatting.imageset/Formatting.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 246 KiB |
21
Swiftgram/SGPayWall/Images.xcassets/ProDetailsIcons.imageset/Contents.json
vendored
Normal file
21
Swiftgram/SGPayWall/Images.xcassets/ProDetailsIcons.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Icons.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
Swiftgram/SGPayWall/Images.xcassets/ProDetailsIcons.imageset/Icons.png
vendored
Normal file
BIN
Swiftgram/SGPayWall/Images.xcassets/ProDetailsIcons.imageset/Icons.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 254 KiB |
21
Swiftgram/SGPayWall/Images.xcassets/ProDetailsMute.imageset/Contents.json
vendored
Normal file
21
Swiftgram/SGPayWall/Images.xcassets/ProDetailsMute.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Mute.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
Swiftgram/SGPayWall/Images.xcassets/ProDetailsMute.imageset/Mute.png
vendored
Normal file
BIN
Swiftgram/SGPayWall/Images.xcassets/ProDetailsMute.imageset/Mute.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 703 KiB |
@ -13,7 +13,7 @@ import TelegramUIPreferences
|
|||||||
|
|
||||||
|
|
||||||
@available(iOS 13.0, *)
|
@available(iOS 13.0, *)
|
||||||
public func sgPayWallController(statusSignal: Signal<Int64, NoError>, replacementController: ViewController, presentationData: PresentationData? = nil, SGIAPManager: SGIAPManager, openUrl: @escaping (String) -> Void, paymentsEnabled: Bool, canBuyInBeta: Bool, openAppStorePage: @escaping () -> Void) -> ViewController {
|
public func sgPayWallController(statusSignal: Signal<Int64, NoError>, replacementController: ViewController, presentationData: PresentationData? = nil, SGIAPManager: SGIAPManager, openUrl: @escaping (String, Bool) -> Void /* url, forceExternal */, paymentsEnabled: Bool, canBuyInBeta: Bool, openAppStorePage: @escaping () -> Void, proSupportUrl: String?) -> ViewController {
|
||||||
// let theme = presentationData?.theme ?? (UITraitCollection.current.userInterfaceStyle == .dark ? defaultDarkColorPresentationTheme : defaultPresentationTheme)
|
// let theme = presentationData?.theme ?? (UITraitCollection.current.userInterfaceStyle == .dark ? defaultDarkColorPresentationTheme : defaultPresentationTheme)
|
||||||
let theme = defaultDarkColorPresentationTheme
|
let theme = defaultDarkColorPresentationTheme
|
||||||
let strings = presentationData?.strings ?? defaultPresentationStrings
|
let strings = presentationData?.strings ?? defaultPresentationStrings
|
||||||
@ -26,11 +26,12 @@ public func sgPayWallController(statusSignal: Signal<Int64, NoError>, replacemen
|
|||||||
// legacyController.displayNavigationBar = false
|
// legacyController.displayNavigationBar = false
|
||||||
legacyController.statusBar.statusBarStyle = .White
|
legacyController.statusBar.statusBarStyle = .White
|
||||||
legacyController.attemptNavigation = { _ in return false }
|
legacyController.attemptNavigation = { _ in return false }
|
||||||
|
legacyController.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||||
|
|
||||||
let swiftUIView = SGSwiftUIView<SGPayWallView>(
|
let swiftUIView = SGSwiftUIView<SGPayWallView>(
|
||||||
legacyController: legacyController,
|
legacyController: legacyController,
|
||||||
content: {
|
content: {
|
||||||
SGPayWallView(wrapperController: legacyController, replacementController: replacementController, SGIAP: SGIAPManager, statusSignal: statusSignal, openUrl: openUrl, openAppStorePage: openAppStorePage, paymentsEnabled: paymentsEnabled, canBuyInBeta: canBuyInBeta)
|
SGPayWallView(wrapperController: legacyController, replacementController: replacementController, SGIAP: SGIAPManager, statusSignal: statusSignal, openUrl: openUrl, openAppStorePage: openAppStorePage, paymentsEnabled: paymentsEnabled, canBuyInBeta: canBuyInBeta, proSupportUrl: proSupportUrl)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let controller = UIHostingController(rootView: swiftUIView, ignoreSafeArea: true)
|
let controller = UIHostingController(rootView: swiftUIView, ignoreSafeArea: true)
|
||||||
@ -95,6 +96,226 @@ struct BackgroundView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
struct SGPayWallFeatureDetails: View {
|
||||||
|
|
||||||
|
let dismissAction: () -> Void
|
||||||
|
var bottomOffset: CGFloat = 0.0
|
||||||
|
let contentHeight: CGFloat = 690.0
|
||||||
|
let features: [SGProFeature]
|
||||||
|
|
||||||
|
@State var shownFeature: SGProFeatureId?
|
||||||
|
// Add animation states
|
||||||
|
@State private var showBackground = false
|
||||||
|
@State private var showContent = false
|
||||||
|
|
||||||
|
@State private var dragOffset: CGFloat = 0
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack(alignment: .bottom) {
|
||||||
|
// Background overlay
|
||||||
|
if showBackground {
|
||||||
|
Color.black.opacity(0.4)
|
||||||
|
.zIndex(0)
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
.onTapGesture {
|
||||||
|
dismissWithAnimation()
|
||||||
|
}
|
||||||
|
.transition(.opacity)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom sheet content
|
||||||
|
if showContent {
|
||||||
|
VStack {
|
||||||
|
if #available(iOS 14.0, *) {
|
||||||
|
TabView(selection: $shownFeature) {
|
||||||
|
ForEach(features) { feature in
|
||||||
|
ScrollView(showsIndicators: false) {
|
||||||
|
SGProFeatureView(
|
||||||
|
feature: feature
|
||||||
|
)
|
||||||
|
Color.clear.frame(height: 8.0) // paginator padding
|
||||||
|
}
|
||||||
|
.tag(feature.id)
|
||||||
|
.scrollBounceBehaviorIfAvailable(.basedOnSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tabViewStyle(.page)
|
||||||
|
.padding(.bottom, bottomOffset - 8.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spacer for purchase buttons
|
||||||
|
if !bottomOffset.isZero {
|
||||||
|
Color.clear.frame(height: bottomOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.zIndex(1)
|
||||||
|
.frame(maxHeight: contentHeight)
|
||||||
|
.background(Color(.black))
|
||||||
|
.cornerRadius(8, corners: [.topLeft, .topRight])
|
||||||
|
.overlay(closeButtonView)
|
||||||
|
.offset(y: max(0, dragOffset))
|
||||||
|
.gesture(
|
||||||
|
DragGesture()
|
||||||
|
.onChanged { value in
|
||||||
|
// Only track downward movement
|
||||||
|
if value.translation.height > 0 {
|
||||||
|
dragOffset = value.translation.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onEnded { value in
|
||||||
|
// If dragged down more than 150 points or with significant velocity, dismiss
|
||||||
|
if value.translation.height > 150 || value.predictedEndTranslation.height > 200 {
|
||||||
|
dismissWithAnimation()
|
||||||
|
} else {
|
||||||
|
// Otherwise, reset position
|
||||||
|
withAnimation(.spring()) {
|
||||||
|
dragOffset = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.transition(.move(edge: .bottom))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
appearWithAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func appearWithAnimation() {
|
||||||
|
withAnimation(.easeIn(duration: 0.2)) {
|
||||||
|
showBackground = true
|
||||||
|
}
|
||||||
|
|
||||||
|
withAnimation(.spring(duration: 0.3)/*.delay(0.1)*/) {
|
||||||
|
showContent = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dismissWithAnimation() {
|
||||||
|
withAnimation(.spring()) {
|
||||||
|
showContent = false
|
||||||
|
dragOffset = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
withAnimation(.easeOut(duration: 0.2).delay(0.1)) {
|
||||||
|
showBackground = false
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
||||||
|
dismissAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var closeButtonView: some View {
|
||||||
|
Button(action: {
|
||||||
|
dismissWithAnimation()
|
||||||
|
}) {
|
||||||
|
Image(systemName: "xmark")
|
||||||
|
.font(.headline)
|
||||||
|
.foregroundColor(.secondary.opacity(0.6))
|
||||||
|
.frame(width: 44, height: 44)
|
||||||
|
.contentShape(Rectangle()) // Improve tappable area
|
||||||
|
}
|
||||||
|
.opacity(showContent ? 1.0 : 0.0)
|
||||||
|
.padding([.top, .trailing], 8)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
struct SGProFeatureView: View {
|
||||||
|
let feature: SGProFeature
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 16) {
|
||||||
|
feature.image
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: 400.0, alignment: .top)
|
||||||
|
.clipped()
|
||||||
|
|
||||||
|
VStack(alignment: .center, spacing: 8) {
|
||||||
|
Text(feature.title)
|
||||||
|
.font(.title)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
Text(featureSubtitle)
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var featureSubtitle: String {
|
||||||
|
return feature.description ?? feature.subtitle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SGProFeatureId: Hashable {
|
||||||
|
case backup
|
||||||
|
case filter
|
||||||
|
case notifications
|
||||||
|
case toolbar
|
||||||
|
case icons
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
struct SGProFeature: Identifiable {
|
||||||
|
|
||||||
|
let id: SGProFeatureId
|
||||||
|
let title: String
|
||||||
|
let subtitle: String
|
||||||
|
let description: String?
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
public var icon: some View {
|
||||||
|
switch (id) {
|
||||||
|
case .backup:
|
||||||
|
FeatureIcon(icon: "lock.fill", backgroundColor: .blue)
|
||||||
|
case .filter:
|
||||||
|
FeatureIcon(icon: "nosign", backgroundColor: .gray, fontWeight: .bold)
|
||||||
|
case .notifications:
|
||||||
|
FeatureIcon(icon: "bell.badge.slash.fill", backgroundColor: .red)
|
||||||
|
case .toolbar:
|
||||||
|
FeatureIcon(icon: "bold.underline", backgroundColor: .blue, iconSize: 16)
|
||||||
|
case .icons:
|
||||||
|
Image("SwiftgramSettings")
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 32, height: 32)
|
||||||
|
@unknown default:
|
||||||
|
Image("SwiftgramPro")
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 32, height: 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var image: Image {
|
||||||
|
switch (id) {
|
||||||
|
case .backup:
|
||||||
|
return Image("ProDetailsBackup")
|
||||||
|
case .filter:
|
||||||
|
return Image("ProDetailsFilter")
|
||||||
|
case .notifications:
|
||||||
|
return Image("ProDetailsMute")
|
||||||
|
case .toolbar:
|
||||||
|
return Image("ProDetailsFormatting")
|
||||||
|
case .icons:
|
||||||
|
return Image("ProDetailsIcons")
|
||||||
|
@unknown default:
|
||||||
|
return Image("pro")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@available(iOS 13.0, *)
|
@available(iOS 13.0, *)
|
||||||
struct SGPayWallView: View {
|
struct SGPayWallView: View {
|
||||||
@Environment(\.navigationBarHeight) var navigationBarHeight: CGFloat
|
@Environment(\.navigationBarHeight) var navigationBarHeight: CGFloat
|
||||||
@ -105,10 +326,11 @@ struct SGPayWallView: View {
|
|||||||
let replacementController: ViewController
|
let replacementController: ViewController
|
||||||
let SGIAP: SGIAPManager
|
let SGIAP: SGIAPManager
|
||||||
let statusSignal: Signal<Int64, NoError>
|
let statusSignal: Signal<Int64, NoError>
|
||||||
let openUrl: (String) -> Void
|
let openUrl: (String, Bool) -> Void // url, forceExternal
|
||||||
let openAppStorePage: () -> Void
|
let openAppStorePage: () -> Void
|
||||||
let paymentsEnabled: Bool
|
let paymentsEnabled: Bool
|
||||||
let canBuyInBeta: Bool
|
let canBuyInBeta: Bool
|
||||||
|
let proSupportUrl: String?
|
||||||
|
|
||||||
private enum PayWallState: Equatable {
|
private enum PayWallState: Equatable {
|
||||||
case ready // ready to buy
|
case ready // ready to buy
|
||||||
@ -123,6 +345,8 @@ struct SGPayWallView: View {
|
|||||||
@State private var state: PayWallState = .ready
|
@State private var state: PayWallState = .ready
|
||||||
@State private var showErrorAlert: Bool = false
|
@State private var showErrorAlert: Bool = false
|
||||||
@State private var showConfetti: Bool = false
|
@State private var showConfetti: Bool = false
|
||||||
|
@State private var showDetails: Bool = false
|
||||||
|
@State private var shownFeature: SGProFeatureId? = nil
|
||||||
|
|
||||||
private let productsPub = NotificationCenter.default.publisher(for: .SGIAPHelperProductsUpdatedNotification, object: nil)
|
private let productsPub = NotificationCenter.default.publisher(for: .SGIAPHelperProductsUpdatedNotification, object: nil)
|
||||||
private let buyOrRestoreSuccessPub = NotificationCenter.default.publisher(for: .SGIAPHelperPurchaseNotification, object: nil)
|
private let buyOrRestoreSuccessPub = NotificationCenter.default.publisher(for: .SGIAPHelperPurchaseNotification, object: nil)
|
||||||
@ -134,6 +358,18 @@ struct SGPayWallView: View {
|
|||||||
@State private var hapticFeedback: HapticFeedback?
|
@State private var hapticFeedback: HapticFeedback?
|
||||||
private let confettiDuration: Double = 5.0
|
private let confettiDuration: Double = 5.0
|
||||||
|
|
||||||
|
@State private var purchaseSectionSize: CGSize = .zero
|
||||||
|
|
||||||
|
private var features: [SGProFeature] {
|
||||||
|
return [
|
||||||
|
SGProFeature(id: .backup, title: "PayWall.SessionBackup.Title".i18n(lang), subtitle: "PayWall.SessionBackup.Notice".i18n(lang), description: "PayWall.SessionBackup.Description".i18n(lang)),
|
||||||
|
SGProFeature(id: .filter, title: "PayWall.MessageFilter.Title".i18n(lang), subtitle: "PayWall.MessageFilter.Notice".i18n(lang), description: "PayWall.MessageFilter.Description".i18n(lang)),
|
||||||
|
SGProFeature(id: .notifications, title: "PayWall.Notifications.Title".i18n(lang), subtitle: "PayWall.Notifications.Notice".i18n(lang), description: "PayWall.Notifications.Description".i18n(lang)),
|
||||||
|
SGProFeature(id: .toolbar, title: "PayWall.InputToolbar.Title".i18n(lang), subtitle: "PayWall.InputToolbar.Notice".i18n(lang), description: "PayWall.InputToolbar.Description".i18n(lang)),
|
||||||
|
SGProFeature(id: .icons, title: "PayWall.AppIcons.Title".i18n(lang), subtitle: "PayWall.AppIcons.Notice".i18n(lang), description: nil)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
BackgroundView()
|
BackgroundView()
|
||||||
@ -178,15 +414,24 @@ struct SGPayWallView: View {
|
|||||||
|
|
||||||
|
|
||||||
// Spacer for purchase buttons
|
// Spacer for purchase buttons
|
||||||
Color.clear.frame(height: 50)
|
Color.clear.frame(height: (purchaseSectionSize.height / 2.0))
|
||||||
}
|
}
|
||||||
.padding(.vertical, 50)
|
.padding(.vertical, (purchaseSectionSize.height / 2.0))
|
||||||
}
|
}
|
||||||
.padding(.leading, max(innerShadowWidth + 8.0, sgLeftSafeAreaInset(containerViewLayout)))
|
.padding(.leading, max(innerShadowWidth + 8.0, sgLeftSafeAreaInset(containerViewLayout)))
|
||||||
.padding(.trailing, max(innerShadowWidth + 8.0, sgRightSafeAreaInset(containerViewLayout)))
|
.padding(.trailing, max(innerShadowWidth + 8.0, sgRightSafeAreaInset(containerViewLayout)))
|
||||||
|
|
||||||
|
if showDetails {
|
||||||
|
SGPayWallFeatureDetails(
|
||||||
|
dismissAction: dismissDetails,
|
||||||
|
bottomOffset: (purchaseSectionSize.height / 2.0) * 0.9, // reduced offset for paginator
|
||||||
|
features: features,
|
||||||
|
shownFeature: shownFeature)
|
||||||
|
}
|
||||||
|
|
||||||
// Fixed purchase button at bottom
|
// Fixed purchase button at bottom
|
||||||
purchaseSection
|
purchaseSection
|
||||||
|
.trackSize($purchaseSectionSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.confetti(isActive: $showConfetti, duration: confettiDuration)
|
.confetti(isActive: $showConfetti, duration: confettiDuration)
|
||||||
@ -248,37 +493,16 @@ struct SGPayWallView: View {
|
|||||||
|
|
||||||
private var featuresSection: some View {
|
private var featuresSection: some View {
|
||||||
VStack(spacing: 8) {
|
VStack(spacing: 8) {
|
||||||
FeatureRow(
|
ForEach(features) { feature in
|
||||||
icon: FeatureIcon(icon: "lock.fill", backgroundColor: .blue),
|
FeatureRow(
|
||||||
title: "PayWall.SessionBackup.Title".i18n(lang),
|
icon: feature.icon,
|
||||||
subtitle: "PayWall.SessionBackup.Notice".i18n(lang)
|
title: feature.title,
|
||||||
)
|
subtitle: feature.subtitle,
|
||||||
|
action: {
|
||||||
FeatureRow(
|
showDetailsForFeature(feature.id)
|
||||||
icon: FeatureIcon(icon: "nosign", backgroundColor: .gray, fontWeight: .bold),
|
}
|
||||||
title: "PayWall.MessageFilter.Title".i18n(lang),
|
)
|
||||||
subtitle: "PayWall.MessageFilter.Notice".i18n(lang)
|
}
|
||||||
)
|
|
||||||
|
|
||||||
FeatureRow(
|
|
||||||
icon: FeatureIcon(icon: "bell.badge.slash.fill", backgroundColor: .red),
|
|
||||||
title: "PayWall.Notifications.Title".i18n(lang),
|
|
||||||
subtitle: "PayWall.Notifications.Notice".i18n(lang)
|
|
||||||
)
|
|
||||||
|
|
||||||
FeatureRow(
|
|
||||||
icon: FeatureIcon(icon: "bold.underline", backgroundColor: .blue, iconSize: 16),
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,20 +520,36 @@ struct SGPayWallView: View {
|
|||||||
private var purchaseSection: some View {
|
private var purchaseSection: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
Divider()
|
Divider()
|
||||||
|
VStack(spacing: 8) {
|
||||||
Button(action: handlePurchase) {
|
Button(action: handlePurchase) {
|
||||||
Text(buttonTitle)
|
Text(buttonTitle)
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding()
|
.padding()
|
||||||
.background(Color(hex: accentColorHex))
|
.background(Color(hex: accentColorHex))
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.cornerRadius(12)
|
.cornerRadius(12)
|
||||||
|
}
|
||||||
|
.disabled((state != .ready || !canPurchase) && !(currentStatus > 1))
|
||||||
|
.opacity(((state != .ready || !canPurchase) && !(currentStatus > 1)) ? 0.5 : 1.0)
|
||||||
|
|
||||||
|
if let proSupportUrl = proSupportUrl {
|
||||||
|
HStack(alignment: .center, spacing: 4) {
|
||||||
|
Text("PayWall.ProSupport.Title".i18n(lang))
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
Button(action: {
|
||||||
|
openUrl(proSupportUrl, false)
|
||||||
|
}) {
|
||||||
|
Text("PayWall.ProSupport.Contact".i18n(lang))
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(Color(hex: accentColorHex))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.disabled((state != .ready || !canPurchase) && !(currentStatus > 1))
|
|
||||||
.opacity(((state != .ready || !canPurchase) && !(currentStatus > 1)) ? 0.5 : 1.0)
|
|
||||||
.padding([.horizontal, .top])
|
.padding([.horizontal, .top])
|
||||||
.padding(.bottom, sgBottomSafeAreaInset(containerViewLayout))
|
.padding(.bottom, sgBottomSafeAreaInset(containerViewLayout) + 2.0)
|
||||||
}
|
}
|
||||||
.foregroundColor(Color.black)
|
.foregroundColor(Color.black)
|
||||||
.backgroundIfAvailable(material: .ultraThinMaterial)
|
.backgroundIfAvailable(material: .ultraThinMaterial)
|
||||||
@ -324,7 +564,7 @@ struct SGPayWallView: View {
|
|||||||
.tint(Color(hex: accentColorHex))
|
.tint(Color(hex: accentColorHex))
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
.environment(\.openURL, OpenURLAction { url in
|
.environment(\.openURL, OpenURLAction { url in
|
||||||
openUrl(url.absoluteString)
|
openUrl(url.absoluteString, false)
|
||||||
return .handled
|
return .handled
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -333,14 +573,14 @@ struct SGPayWallView: View {
|
|||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
HStack(alignment: .top, spacing: 8) {
|
HStack(alignment: .top, spacing: 8) {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
openUrl("PayWall.PrivacyURL".i18n(lang))
|
openUrl("PayWall.PrivacyURL".i18n(lang), true)
|
||||||
}) {
|
}) {
|
||||||
Text("PayWall.Privacy".i18n(lang))
|
Text("PayWall.Privacy".i18n(lang))
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundColor(Color(hex: accentColorHex))
|
.foregroundColor(Color(hex: accentColorHex))
|
||||||
}
|
}
|
||||||
Button(action: {
|
Button(action: {
|
||||||
openUrl("PayWall.TermsURL".i18n(lang))
|
openUrl("PayWall.TermsURL".i18n(lang), true)
|
||||||
}) {
|
}) {
|
||||||
Text("PayWall.Terms".i18n(lang))
|
Text("PayWall.Terms".i18n(lang))
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
@ -369,7 +609,7 @@ struct SGPayWallView: View {
|
|||||||
}
|
}
|
||||||
HStack {
|
HStack {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
openUrl("PayWall.About.SignatureURL".i18n(lang))
|
openUrl("PayWall.About.SignatureURL".i18n(lang), false)
|
||||||
}) {
|
}) {
|
||||||
Text("PayWall.About.Signature".i18n(lang))
|
Text("PayWall.About.Signature".i18n(lang))
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
@ -390,6 +630,8 @@ struct SGPayWallView: View {
|
|||||||
.frame(width: 44, height: 44)
|
.frame(width: 44, height: 44)
|
||||||
.contentShape(Rectangle()) // Improve tappable area
|
.contentShape(Rectangle()) // Improve tappable area
|
||||||
}
|
}
|
||||||
|
.disabled(showDetails)
|
||||||
|
.opacity(showDetails ? 0.0 : 1.0)
|
||||||
.padding([.top, .trailing], 16)
|
.padding([.top, .trailing], 16)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
|
||||||
}
|
}
|
||||||
@ -426,6 +668,18 @@ struct SGPayWallView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func showDetailsForFeature(_ featureId: SGProFeatureId) {
|
||||||
|
if #available(iOS 14.0, *) {
|
||||||
|
shownFeature = featureId
|
||||||
|
showDetails = true
|
||||||
|
} // pagination is not available on iOS 13
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dismissDetails() {
|
||||||
|
// shownFeature = nil
|
||||||
|
showDetails = false
|
||||||
|
}
|
||||||
|
|
||||||
private func updateSelectedProduct() {
|
private func updateSelectedProduct() {
|
||||||
product = SGIAP.availableProducts.first { $0.id == SG_CONFIG.iaps.first ?? "" }
|
product = SGIAP.availableProducts.first { $0.id == SG_CONFIG.iaps.first ?? "" }
|
||||||
}
|
}
|
||||||
@ -515,11 +769,10 @@ struct FeatureRow<IconContent: View>: View {
|
|||||||
let icon: IconContent
|
let icon: IconContent
|
||||||
let title: String
|
let title: String
|
||||||
let subtitle: String
|
let subtitle: String
|
||||||
|
let action: () -> Void
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Button(action: {
|
Button(action: action) {
|
||||||
// TODO(swiftgram): Feature row clarification
|
|
||||||
}) {
|
|
||||||
HStack(spacing: 16) {
|
HStack(spacing: 16) {
|
||||||
|
|
||||||
HStack(alignment: .top, spacing: 12) {
|
HStack(alignment: .top, spacing: 12) {
|
||||||
@ -537,10 +790,11 @@ struct FeatureRow<IconContent: View>: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
// TODO(swiftgram): uncomment
|
if #available(iOS 14.0, *) {
|
||||||
// Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
// .font(.system(size: 12, weight: .semibold))
|
.font(.system(size: 12, weight: .semibold))
|
||||||
// .foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
|
} // Descriptions are not available on iOS 13
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.background(
|
.background(
|
||||||
|
@ -195,25 +195,33 @@
|
|||||||
|
|
||||||
"PayWall.SessionBackup.Title" = "Accounts Backup";
|
"PayWall.SessionBackup.Title" = "Accounts Backup";
|
||||||
"PayWall.SessionBackup.Notice" = "Log-in to accounts without code, even after reinstall. Secure storage with on-device Keychain.";
|
"PayWall.SessionBackup.Notice" = "Log-in to accounts without code, even after reinstall. Secure storage with on-device Keychain.";
|
||||||
|
"PayWall.SessionBackup.Description" = "Changing device or deleting Swiftgram is no longer an issue. Restore all Sessions that are still Active on Telegram servers.";
|
||||||
|
|
||||||
"PayWall.MessageFilter.Title" = "Message Filter";
|
"PayWall.MessageFilter.Title" = "Message Filter";
|
||||||
"PayWall.MessageFilter.Notice" = "Reduce visibility of SPAM, promotions and annoying messages.";
|
"PayWall.MessageFilter.Notice" = "Reduce visibility of SPAM, promotions and annoying messages.";
|
||||||
|
"PayWall.MessageFilter.Description" = "Create a list of keywords you don't want to see often and Swiftgram will reduce distractions.";
|
||||||
|
|
||||||
"PayWall.Notifications.Title" = "Disable @mentions and replies";
|
"PayWall.Notifications.Title" = "Disable @mentions and replies";
|
||||||
"PayWall.Notifications.Notice" = "Hide or mute non-important notifications.";
|
"PayWall.Notifications.Notice" = "Hide or mute non-important notifications.";
|
||||||
|
"PayWall.Notifications.Description" = "No more Pinned Messages or @mentions when you need some piece of mind.";
|
||||||
|
|
||||||
"PayWall.InputToolbar.Title" = "Formatting Panel";
|
"PayWall.InputToolbar.Title" = "Formatting Panel";
|
||||||
"PayWall.InputToolbar.Notice" = "Save time formatting messages with just a single tap.";
|
"PayWall.InputToolbar.Notice" = "Save time formatting messages with just a single tap.";
|
||||||
|
"PayWall.InputToolbar.Description" = "Apply and clear Formatting or insert new lines like a Pro.";
|
||||||
|
|
||||||
"PayWall.AppIcons.Title" = "Unique App Icons";
|
"PayWall.AppIcons.Title" = "Unique App Icons";
|
||||||
"PayWall.AppIcons.Notice" = "Customize Swiftgram look on your home screen.";
|
"PayWall.AppIcons.Notice" = "Customize Swiftgram look on your home screen.";
|
||||||
|
|
||||||
"PayWall.About.Title" = "About Swiftgram Pro";
|
"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 expensive 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.";
|
"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 expensive 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.";
|
||||||
|
/* DO NOT TRANSLATE */
|
||||||
"PayWall.About.Signature" = "@Kylmakalle";
|
"PayWall.About.Signature" = "@Kylmakalle";
|
||||||
/* DO NOT TRANSLATE */
|
/* DO NOT TRANSLATE */
|
||||||
"PayWall.About.SignatureURL" = "https://t.me/Kylmakalle";
|
"PayWall.About.SignatureURL" = "https://t.me/Kylmakalle";
|
||||||
|
|
||||||
|
"PayWall.ProSupport.Title" = "Troubles with payment?";
|
||||||
|
"PayWall.ProSupport.Contact" = "No worries!";
|
||||||
|
|
||||||
"PayWall.RestorePurchases" = "Restore Purchases";
|
"PayWall.RestorePurchases" = "Restore Purchases";
|
||||||
"PayWall.Terms" = "Terms of Service";
|
"PayWall.Terms" = "Terms of Service";
|
||||||
"PayWall.Privacy" = "Privacy Policy";
|
"PayWall.Privacy" = "Privacy Policy";
|
||||||
|
@ -413,6 +413,21 @@ public enum BackgroundMaterial {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum BounceBehavior {
|
||||||
|
case automatic
|
||||||
|
case always
|
||||||
|
case basedOnSize
|
||||||
|
|
||||||
|
@available(iOS 16.4, *)
|
||||||
|
var behavior: ScrollBounceBehavior {
|
||||||
|
switch self {
|
||||||
|
case .automatic: return .automatic
|
||||||
|
case .always: return .always
|
||||||
|
case .basedOnSize: return .basedOnSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@available(iOS 13.0, *)
|
@available(iOS 13.0, *)
|
||||||
public extension View {
|
public extension View {
|
||||||
@ -437,3 +452,62 @@ public extension View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
public extension View {
|
||||||
|
func scrollBounceBehaviorIfAvailable(_ behavior: BounceBehavior) -> some View {
|
||||||
|
if #available(iOS 16.4, *) {
|
||||||
|
return self.scrollBounceBehavior(behavior.behavior)
|
||||||
|
} else {
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
public extension View {
|
||||||
|
func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
|
||||||
|
clipShape(RoundedCorner(radius: radius, corners: corners))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
public struct RoundedCorner: Shape {
|
||||||
|
var radius: CGFloat = .infinity
|
||||||
|
var corners: UIRectCorner = .allCorners
|
||||||
|
|
||||||
|
public func path(in rect: CGRect) -> Path {
|
||||||
|
let path = UIBezierPath(
|
||||||
|
roundedRect: rect,
|
||||||
|
byRoundingCorners: corners,
|
||||||
|
cornerRadii: CGSize(width: radius, height: radius)
|
||||||
|
)
|
||||||
|
return Path(path.cgPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
public struct ContentSizeModifier: ViewModifier {
|
||||||
|
@Binding var size: CGSize
|
||||||
|
|
||||||
|
public func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.background(
|
||||||
|
GeometryReader { geometry -> Color in
|
||||||
|
if geometry.size != size {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.size = geometry.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Color.clear
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
public extension View {
|
||||||
|
func trackSize(_ size: Binding<CGSize>) -> some View {
|
||||||
|
self.modifier(ContentSizeModifier(size: size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@ public struct SGWebSettings: Codable, Equatable {
|
|||||||
public let user: SGUserSettings
|
public let user: SGUserSettings
|
||||||
|
|
||||||
public static var defaultValue: SGWebSettings {
|
public static var defaultValue: SGWebSettings {
|
||||||
return SGWebSettings(global: SGGlobalSettings(ytPip: true, qrLogin: true, storiesAvailable: false, canViewMessages: true, canEditSettings: false, canShowTelescope: false, announcementsData: nil, regdateFormat: "month", botMonkeys: [], forceReasons: [], unforceReasons: [], paymentsEnabled: true, duckyAppIconAvailable: true, canGrant: false), user: SGUserSettings(contentReasons: [], canSendTelescope: false, canBuyInBeta: true))
|
return SGWebSettings(global: SGGlobalSettings(ytPip: true, qrLogin: true, storiesAvailable: false, canViewMessages: true, canEditSettings: false, canShowTelescope: false, announcementsData: nil, regdateFormat: "month", botMonkeys: [], forceReasons: [], unforceReasons: [], paymentsEnabled: true, duckyAppIconAvailable: true, canGrant: false, proSupportUrl: nil), user: SGUserSettings(contentReasons: [], canSendTelescope: false, canBuyInBeta: true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +24,7 @@ public struct SGGlobalSettings: Codable, Equatable {
|
|||||||
public let paymentsEnabled: Bool
|
public let paymentsEnabled: Bool
|
||||||
public let duckyAppIconAvailable: Bool
|
public let duckyAppIconAvailable: Bool
|
||||||
public let canGrant: Bool
|
public let canGrant: Bool
|
||||||
|
public let proSupportUrl: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct SGBotMonkeys: Codable, Equatable {
|
public struct SGBotMonkeys: Codable, Equatable {
|
||||||
|
@ -4,6 +4,8 @@ import SGConfig
|
|||||||
import SGSettingsUI
|
import SGSettingsUI
|
||||||
import SGDebugUI
|
import SGDebugUI
|
||||||
import SFSafariViewControllerPlus
|
import SFSafariViewControllerPlus
|
||||||
|
import UndoUI
|
||||||
|
//
|
||||||
import ContactListUI
|
import ContactListUI
|
||||||
import Foundation
|
import Foundation
|
||||||
import Display
|
import Display
|
||||||
@ -1041,6 +1043,33 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case "restart":
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let lang = presentationData.strings.baseLanguageCode
|
||||||
|
context.sharedContext.presentGlobalController(
|
||||||
|
UndoOverlayController(
|
||||||
|
presentationData: presentationData,
|
||||||
|
content: .info(title: nil,
|
||||||
|
text: "Common.RestartRequired".i18n(lang),
|
||||||
|
timeout: nil,
|
||||||
|
customUndoText: "Common.RestartNow".i18n(lang)
|
||||||
|
),
|
||||||
|
elevatedLayout: false,
|
||||||
|
action: { action in if action == .undo { exit(0) }; return true }
|
||||||
|
),
|
||||||
|
nil
|
||||||
|
)
|
||||||
|
case "restore_purchases", "pro_restore", "validate", "restore":
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let lang = presentationData.strings.baseLanguageCode
|
||||||
|
context.sharedContext.presentGlobalController(UndoOverlayController(
|
||||||
|
presentationData: presentationData,
|
||||||
|
content: .info(title: nil, text: "PayWall.Button.Restoring".i18n(lang), timeout: nil, customUndoText: nil),
|
||||||
|
elevatedLayout: false,
|
||||||
|
action: { _ in return false }
|
||||||
|
),
|
||||||
|
nil)
|
||||||
|
context.sharedContext.SGIAP?.restorePurchases {}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -3625,7 +3625,23 @@ extension SharedAccountContextImpl {
|
|||||||
|
|
||||||
let proController = self.makeSGProController(context: context)
|
let proController = self.makeSGProController(context: context)
|
||||||
let sgWebSettings = context.currentAppConfiguration.with { $0 }.sgWebSettings
|
let sgWebSettings = context.currentAppConfiguration.with { $0 }.sgWebSettings
|
||||||
let payWallController = sgPayWallController(statusSignal: statusSignal, replacementController: proController, presentationData: self.currentPresentationData.with { $0 }, SGIAPManager: sgIAP, openUrl: self.applicationBindings.openUrl, paymentsEnabled: sgWebSettings.global.paymentsEnabled, canBuyInBeta: sgWebSettings.user.canBuyInBeta, openAppStorePage: self.applicationBindings.openAppStorePage)
|
let presentationData = self.currentPresentationData.with { $0 }
|
||||||
|
var payWallController: ViewController? = nil
|
||||||
|
let openUrl: ((String, Bool) -> Void) = { [weak self, weak context] url, forceExternal in
|
||||||
|
guard let strongSelf = self, let strongContext = context, let strongPayWallController = payWallController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let navigationController = strongPayWallController.navigationController as? NavigationController
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
strongSelf.openExternalUrl(context: strongContext, urlContext: .generic, url: url, forceExternal: forceExternal, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var supportUrl: String? = nil
|
||||||
|
if let supportUrlString = sgWebSettings.global.proSupportUrl, !supportUrlString.isEmpty, let data = Data(base64Encoded: supportUrlString), let decodedString = String(data: data, encoding: .utf8) {
|
||||||
|
supportUrl = decodedString
|
||||||
|
}
|
||||||
|
payWallController = sgPayWallController(statusSignal: statusSignal, replacementController: proController, presentationData: presentationData, SGIAPManager: sgIAP, openUrl: openUrl, paymentsEnabled: sgWebSettings.global.paymentsEnabled, canBuyInBeta: sgWebSettings.user.canBuyInBeta, openAppStorePage: self.applicationBindings.openAppStorePage, proSupportUrl: supportUrl)
|
||||||
return payWallController
|
return payWallController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user