|
@@ -2637,7 +2637,7 @@ private extension ViewController {
|
|
2637
|
let contentStack = NSStackView()
|
2637
|
let contentStack = NSStackView()
|
|
2638
|
contentStack.translatesAutoresizingMaskIntoConstraints = false
|
2638
|
contentStack.translatesAutoresizingMaskIntoConstraints = false
|
|
2639
|
contentStack.orientation = .vertical
|
2639
|
contentStack.orientation = .vertical
|
|
2640
|
- contentStack.spacing = 10
|
|
|
|
|
|
2640
|
+ contentStack.spacing = 12
|
|
2641
|
contentStack.distribution = .fill
|
2641
|
contentStack.distribution = .fill
|
|
2642
|
contentStack.alignment = .centerX
|
2642
|
contentStack.alignment = .centerX
|
|
2643
|
panel.addSubview(contentStack)
|
2643
|
panel.addSubview(contentStack)
|
|
@@ -2650,9 +2650,6 @@ private extension ViewController {
|
|
2650
|
topRow.distribution = .fill
|
2650
|
topRow.distribution = .fill
|
|
2651
|
topRow.spacing = 10
|
2651
|
topRow.spacing = 10
|
|
2652
|
topRow.addArrangedSubview(textLabel("Classroom Pro", font: NSFont.systemFont(ofSize: 27, weight: .bold), color: palette.textPrimary))
|
2652
|
topRow.addArrangedSubview(textLabel("Classroom Pro", font: NSFont.systemFont(ofSize: 27, weight: .bold), color: palette.textPrimary))
|
|
2653
|
- let topSpacer = NSView()
|
|
|
|
2654
|
- topSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
2655
|
- topRow.addArrangedSubview(topSpacer)
|
|
|
|
2656
|
let closeButton = HoverButton(title: "✕", target: self, action: #selector(closePaywallClicked(_:)))
|
2653
|
let closeButton = HoverButton(title: "✕", target: self, action: #selector(closePaywallClicked(_:)))
|
|
2657
|
closeButton.translatesAutoresizingMaskIntoConstraints = false
|
2654
|
closeButton.translatesAutoresizingMaskIntoConstraints = false
|
|
2658
|
closeButton.isBordered = false
|
2655
|
closeButton.isBordered = false
|
|
@@ -2674,15 +2671,10 @@ private extension ViewController {
|
|
2674
|
closeButton.layer?.backgroundColor = (hovering ? hover : base).cgColor
|
2671
|
closeButton.layer?.backgroundColor = (hovering ? hover : base).cgColor
|
|
2675
|
closeButton.contentTintColor = hovering ? (self.darkModeEnabled ? .white : self.palette.textPrimary) : self.palette.textSecondary
|
2672
|
closeButton.contentTintColor = hovering ? (self.darkModeEnabled ? .white : self.palette.textPrimary) : self.palette.textSecondary
|
|
2676
|
}
|
2673
|
}
|
|
2677
|
- topRow.addArrangedSubview(closeButton)
|
|
|
|
|
|
2674
|
+ panel.addSubview(closeButton)
|
|
2678
|
contentStack.addArrangedSubview(topRow)
|
2675
|
contentStack.addArrangedSubview(topRow)
|
|
2679
|
topRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
2676
|
topRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
|
2680
|
|
2677
|
|
|
2681
|
- let heroTopSpacer = NSView()
|
|
|
|
2682
|
- heroTopSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
2683
|
- heroTopSpacer.heightAnchor.constraint(equalToConstant: 18).isActive = true
|
|
|
|
2684
|
- contentStack.addArrangedSubview(heroTopSpacer)
|
|
|
|
2685
|
-
|
|
|
|
2686
|
let hero = roundedContainer(cornerRadius: 16, color: palette.sectionCard)
|
2678
|
let hero = roundedContainer(cornerRadius: 16, color: palette.sectionCard)
|
|
2687
|
hero.translatesAutoresizingMaskIntoConstraints = false
|
2679
|
hero.translatesAutoresizingMaskIntoConstraints = false
|
|
2688
|
styleSurface(hero, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
2680
|
styleSurface(hero, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
@@ -2710,11 +2702,6 @@ private extension ViewController {
|
|
2710
|
heroSubtitle.bottomAnchor.constraint(equalTo: hero.bottomAnchor, constant: -16)
|
2702
|
heroSubtitle.bottomAnchor.constraint(equalTo: hero.bottomAnchor, constant: -16)
|
|
2711
|
])
|
2703
|
])
|
|
2712
|
|
2704
|
|
|
2713
|
- let benefitsTopSpacer = NSView()
|
|
|
|
2714
|
- benefitsTopSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
2715
|
- benefitsTopSpacer.heightAnchor.constraint(equalToConstant: 40).isActive = true
|
|
|
|
2716
|
- contentStack.addArrangedSubview(benefitsTopSpacer)
|
|
|
|
2717
|
-
|
|
|
|
2718
|
let benefitsRow = NSStackView()
|
2705
|
let benefitsRow = NSStackView()
|
|
2719
|
benefitsRow.translatesAutoresizingMaskIntoConstraints = false
|
2706
|
benefitsRow.translatesAutoresizingMaskIntoConstraints = false
|
|
2720
|
benefitsRow.orientation = .horizontal
|
2707
|
benefitsRow.orientation = .horizontal
|
|
@@ -2728,11 +2715,6 @@ private extension ViewController {
|
|
2728
|
contentStack.addArrangedSubview(benefitsRow)
|
2715
|
contentStack.addArrangedSubview(benefitsRow)
|
|
2729
|
benefitsRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
2716
|
benefitsRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
|
2730
|
|
2717
|
|
|
2731
|
- let midTopSpacer = NSView()
|
|
|
|
2732
|
- midTopSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
2733
|
- midTopSpacer.heightAnchor.constraint(equalToConstant: 6).isActive = true
|
|
|
|
2734
|
- contentStack.addArrangedSubview(midTopSpacer)
|
|
|
|
2735
|
-
|
|
|
|
2736
|
let plansRow = NSStackView()
|
2718
|
let plansRow = NSStackView()
|
|
2737
|
plansRow.translatesAutoresizingMaskIntoConstraints = false
|
2719
|
plansRow.translatesAutoresizingMaskIntoConstraints = false
|
|
2738
|
plansRow.orientation = .horizontal
|
2720
|
plansRow.orientation = .horizontal
|
|
@@ -2784,10 +2766,17 @@ private extension ViewController {
|
|
2784
|
plansRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
2766
|
plansRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
|
2785
|
updatePaywallPlanSelection()
|
2767
|
updatePaywallPlanSelection()
|
|
2786
|
|
2768
|
|
|
2787
|
- let midBottomSpacer = NSView()
|
|
|
|
2788
|
- midBottomSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
2789
|
- midBottomSpacer.heightAnchor.constraint(equalToConstant: 8).isActive = true
|
|
|
|
2790
|
- contentStack.addArrangedSubview(midBottomSpacer)
|
|
|
|
|
|
2769
|
+ let trustRow = NSStackView()
|
|
|
|
2770
|
+ trustRow.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
2771
|
+ trustRow.orientation = .horizontal
|
|
|
|
2772
|
+ trustRow.spacing = 8
|
|
|
|
2773
|
+ trustRow.distribution = .fillEqually
|
|
|
|
2774
|
+ trustRow.alignment = .centerY
|
|
|
|
2775
|
+ trustRow.addArrangedSubview(paywallMetaItem(title: "Cancel anytime", subtitle: "No lock-in"))
|
|
|
|
2776
|
+ trustRow.addArrangedSubview(paywallMetaItem(title: "Instant access", subtitle: "Unlock all tools"))
|
|
|
|
2777
|
+ trustRow.addArrangedSubview(paywallMetaItem(title: "Secure billing", subtitle: "Handled by Apple"))
|
|
|
|
2778
|
+ contentStack.addArrangedSubview(trustRow)
|
|
|
|
2779
|
+ trustRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
|
2791
|
|
2780
|
|
|
2792
|
let offer = textLabel(paywallOfferText(for: selectedPremiumPlan), font: NSFont.systemFont(ofSize: 13, weight: .semibold), color: palette.textPrimary)
|
2781
|
let offer = textLabel(paywallOfferText(for: selectedPremiumPlan), font: NSFont.systemFont(ofSize: 13, weight: .semibold), color: palette.textPrimary)
|
|
2793
|
offer.alignment = .center
|
2782
|
offer.alignment = .center
|
|
@@ -2843,20 +2832,17 @@ private extension ViewController {
|
|
2843
|
secure.bottomAnchor.constraint(equalTo: secureWrap.bottomAnchor, constant: -4)
|
2832
|
secure.bottomAnchor.constraint(equalTo: secureWrap.bottomAnchor, constant: -4)
|
|
2844
|
])
|
2833
|
])
|
|
2845
|
|
2834
|
|
|
2846
|
- let footerTopSpacer = NSView()
|
|
|
|
2847
|
- footerTopSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
2848
|
- contentStack.addArrangedSubview(footerTopSpacer)
|
|
|
|
2849
|
- footerTopSpacer.heightAnchor.constraint(equalToConstant: 4).isActive = true
|
|
|
|
2850
|
-
|
|
|
|
2851
|
let footer = paywallFooterLinks()
|
2835
|
let footer = paywallFooterLinks()
|
|
2852
|
contentStack.addArrangedSubview(footer)
|
2836
|
contentStack.addArrangedSubview(footer)
|
|
2853
|
footer.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
2837
|
footer.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
|
2854
|
|
2838
|
|
|
2855
|
NSLayoutConstraint.activate([
|
2839
|
NSLayoutConstraint.activate([
|
|
|
|
2840
|
+ closeButton.topAnchor.constraint(equalTo: panel.topAnchor, constant: 18),
|
|
|
|
2841
|
+ closeButton.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -18),
|
|
2856
|
contentStack.centerXAnchor.constraint(equalTo: panel.centerXAnchor),
|
2842
|
contentStack.centerXAnchor.constraint(equalTo: panel.centerXAnchor),
|
|
2857
|
contentStack.widthAnchor.constraint(equalToConstant: paywallLayoutWidth),
|
2843
|
contentStack.widthAnchor.constraint(equalToConstant: paywallLayoutWidth),
|
|
2858
|
- contentStack.topAnchor.constraint(equalTo: panel.topAnchor, constant: 52),
|
|
|
|
2859
|
- contentStack.bottomAnchor.constraint(equalTo: panel.bottomAnchor, constant: -10)
|
|
|
|
|
|
2844
|
+ contentStack.topAnchor.constraint(equalTo: panel.topAnchor, constant: 80),
|
|
|
|
2845
|
+ contentStack.bottomAnchor.constraint(equalTo: panel.bottomAnchor, constant: -12)
|
|
2860
|
])
|
2846
|
])
|
|
2861
|
|
2847
|
|
|
2862
|
refreshPaywallStoreUI()
|
2848
|
refreshPaywallStoreUI()
|
|
@@ -3044,6 +3030,27 @@ private extension ViewController {
|
|
3044
|
return stack
|
3030
|
return stack
|
|
3045
|
}
|
3031
|
}
|
|
3046
|
|
3032
|
|
|
|
|
3033
|
+ func paywallMetaItem(title: String, subtitle: String) -> NSView {
|
|
|
|
3034
|
+ let card = roundedContainer(cornerRadius: 12, color: palette.inputBackground)
|
|
|
|
3035
|
+ card.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
3036
|
+ card.heightAnchor.constraint(equalToConstant: 48).isActive = true
|
|
|
|
3037
|
+ styleSurface(card, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
|
|
3038
|
+
|
|
|
|
3039
|
+ let titleLabel = textLabel(title, font: NSFont.systemFont(ofSize: 11, weight: .semibold), color: palette.textPrimary)
|
|
|
|
3040
|
+ let subtitleLabel = textLabel(subtitle, font: NSFont.systemFont(ofSize: 10, weight: .medium), color: palette.textSecondary)
|
|
|
|
3041
|
+ card.addSubview(titleLabel)
|
|
|
|
3042
|
+ card.addSubview(subtitleLabel)
|
|
|
|
3043
|
+
|
|
|
|
3044
|
+ NSLayoutConstraint.activate([
|
|
|
|
3045
|
+ titleLabel.centerXAnchor.constraint(equalTo: card.centerXAnchor),
|
|
|
|
3046
|
+ titleLabel.topAnchor.constraint(equalTo: card.topAnchor, constant: 8),
|
|
|
|
3047
|
+ subtitleLabel.centerXAnchor.constraint(equalTo: card.centerXAnchor),
|
|
|
|
3048
|
+ subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 3)
|
|
|
|
3049
|
+ ])
|
|
|
|
3050
|
+
|
|
|
|
3051
|
+ return card
|
|
|
|
3052
|
+ }
|
|
|
|
3053
|
+
|
|
3047
|
func paywallBenefitItem(icon: String, text: String) -> NSView {
|
3054
|
func paywallBenefitItem(icon: String, text: String) -> NSView {
|
|
3048
|
let card = HoverTrackingView()
|
3055
|
let card = HoverTrackingView()
|
|
3049
|
card.translatesAutoresizingMaskIntoConstraints = false
|
3056
|
card.translatesAutoresizingMaskIntoConstraints = false
|