|
@@ -610,6 +610,7 @@ class ViewController: NSViewController {
|
|
|
super.viewDidLayout()
|
|
super.viewDidLayout()
|
|
|
alignNativeTrafficLights()
|
|
alignNativeTrafficLights()
|
|
|
if paywallOverlayView != nil {
|
|
if paywallOverlayView != nil {
|
|
|
|
|
+ updatePaywallPanelSize()
|
|
|
updatePaywallResponsiveLayout()
|
|
updatePaywallResponsiveLayout()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -3697,15 +3698,15 @@ class ViewController: NSViewController {
|
|
|
styleSurface(panel, borderColor: NSColor.clear, borderWidth: 0, shadow: false)
|
|
styleSurface(panel, borderColor: NSColor.clear, borderWidth: 0, shadow: false)
|
|
|
root.addSubview(panel)
|
|
root.addSubview(panel)
|
|
|
|
|
|
|
|
- // Full-screen: fill the entire app content area.
|
|
|
|
|
- let insetX: CGFloat = 0
|
|
|
|
|
- let insetY: CGFloat = 0
|
|
|
|
|
|
|
+ // Full-screen paywall: fill the entire app content area.
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
- panel.leadingAnchor.constraint(equalTo: root.leadingAnchor, constant: insetX),
|
|
|
|
|
- panel.trailingAnchor.constraint(equalTo: root.trailingAnchor, constant: -insetX),
|
|
|
|
|
- panel.topAnchor.constraint(equalTo: root.topAnchor, constant: insetY),
|
|
|
|
|
- panel.bottomAnchor.constraint(equalTo: root.bottomAnchor, constant: -insetY)
|
|
|
|
|
|
|
+ panel.leadingAnchor.constraint(equalTo: root.leadingAnchor),
|
|
|
|
|
+ panel.trailingAnchor.constraint(equalTo: root.trailingAnchor),
|
|
|
|
|
+ panel.topAnchor.constraint(equalTo: root.topAnchor),
|
|
|
|
|
+ panel.bottomAnchor.constraint(equalTo: root.bottomAnchor)
|
|
|
])
|
|
])
|
|
|
|
|
+ paywallPanelWidthConstraint = nil
|
|
|
|
|
+ paywallPanelHeightConstraint = nil
|
|
|
|
|
|
|
|
// Responsive logic should measure the panel size (not the full-screen root).
|
|
// Responsive logic should measure the panel size (not the full-screen root).
|
|
|
self.paywallPanelView = panel
|
|
self.paywallPanelView = panel
|
|
@@ -3723,7 +3724,7 @@ class ViewController: NSViewController {
|
|
|
topRow.alignment = .centerY
|
|
topRow.alignment = .centerY
|
|
|
topRow.distribution = .fill
|
|
topRow.distribution = .fill
|
|
|
topRow.spacing = 10
|
|
topRow.spacing = 10
|
|
|
- topRow.addArrangedSubview(textLabel("Get Premium", font: NSFont.systemFont(ofSize: 24, weight: .bold), color: primaryText))
|
|
|
|
|
|
|
+ topRow.addArrangedSubview(textLabel("Meetings Pro", font: NSFont.systemFont(ofSize: 24, weight: .bold), color: primaryText))
|
|
|
let topSpacer = NSView()
|
|
let topSpacer = NSView()
|
|
|
topSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
topSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
topRow.addArrangedSubview(topSpacer)
|
|
topRow.addArrangedSubview(topSpacer)
|
|
@@ -3743,18 +3744,20 @@ class ViewController: NSViewController {
|
|
|
closeButton.widthAnchor.constraint(equalToConstant: 28).isActive = true
|
|
closeButton.widthAnchor.constraint(equalToConstant: 28).isActive = true
|
|
|
closeButton.heightAnchor.constraint(equalToConstant: 28).isActive = true
|
|
closeButton.heightAnchor.constraint(equalToConstant: 28).isActive = true
|
|
|
topRow.addArrangedSubview(closeButton)
|
|
topRow.addArrangedSubview(closeButton)
|
|
|
- topRow.widthAnchor.constraint(greaterThanOrEqualToConstant: paywallContentWidth).isActive = true
|
|
|
|
|
contentStack.addArrangedSubview(topRow)
|
|
contentStack.addArrangedSubview(topRow)
|
|
|
|
|
+ topRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
|
|
|
|
|
|
|
let hero = paywallHeroCard(
|
|
let hero = paywallHeroCard(
|
|
|
eyebrow: "PREMIUM EXPERIENCE",
|
|
eyebrow: "PREMIUM EXPERIENCE",
|
|
|
- title: "Elevate your classroom experience",
|
|
|
|
|
- subtitle: "Unlock intelligent automation, deep analytics, and premium support for modern educators."
|
|
|
|
|
|
|
+ title: "Elevate your meetings workflow",
|
|
|
|
|
+ subtitle: "Unlock automation, premium meeting tools, and priority support for every session."
|
|
|
)
|
|
)
|
|
|
contentStack.addArrangedSubview(hero)
|
|
contentStack.addArrangedSubview(hero)
|
|
|
|
|
+ hero.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
|
|
|
|
|
|
|
let features = paywallFeaturesRow()
|
|
let features = paywallFeaturesRow()
|
|
|
contentStack.addArrangedSubview(features)
|
|
contentStack.addArrangedSubview(features)
|
|
|
|
|
+ features.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
|
|
|
|
|
|
|
let bodyStack = NSStackView()
|
|
let bodyStack = NSStackView()
|
|
|
bodyStack.translatesAutoresizingMaskIntoConstraints = false
|
|
bodyStack.translatesAutoresizingMaskIntoConstraints = false
|
|
@@ -3764,6 +3767,7 @@ class ViewController: NSViewController {
|
|
|
bodyStack.distribution = .fill
|
|
bodyStack.distribution = .fill
|
|
|
contentStack.addArrangedSubview(bodyStack)
|
|
contentStack.addArrangedSubview(bodyStack)
|
|
|
paywallBodyStack = bodyStack
|
|
paywallBodyStack = bodyStack
|
|
|
|
|
+ bodyStack.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
|
|
|
|
|
|
|
|
let planColumn = NSStackView()
|
|
let planColumn = NSStackView()
|
|
|
planColumn.translatesAutoresizingMaskIntoConstraints = false
|
|
planColumn.translatesAutoresizingMaskIntoConstraints = false
|
|
@@ -3772,11 +3776,12 @@ class ViewController: NSViewController {
|
|
|
planColumn.alignment = .leading
|
|
planColumn.alignment = .leading
|
|
|
planColumn.distribution = .fill
|
|
planColumn.distribution = .fill
|
|
|
bodyStack.addArrangedSubview(planColumn)
|
|
bodyStack.addArrangedSubview(planColumn)
|
|
|
|
|
+ planColumn.widthAnchor.constraint(equalTo: bodyStack.widthAnchor).isActive = true
|
|
|
|
|
|
|
|
let weeklyCard = paywallPlanCard(
|
|
let weeklyCard = paywallPlanCard(
|
|
|
title: "Weekly",
|
|
title: "Weekly",
|
|
|
price: paywallDisplayPrice(for: .weekly),
|
|
price: paywallDisplayPrice(for: .weekly),
|
|
|
- badge: "Basic Deal",
|
|
|
|
|
|
|
+ badge: "Basic",
|
|
|
badgeColor: NSColor(calibratedRed: 1.0, green: 0.60, blue: 0.20, alpha: 1),
|
|
badgeColor: NSColor(calibratedRed: 1.0, green: 0.60, blue: 0.20, alpha: 1),
|
|
|
subtitle: "Billed weekly",
|
|
subtitle: "Billed weekly",
|
|
|
plan: .weekly,
|
|
plan: .weekly,
|
|
@@ -3785,25 +3790,25 @@ class ViewController: NSViewController {
|
|
|
let monthlyCard = paywallPlanCard(
|
|
let monthlyCard = paywallPlanCard(
|
|
|
title: "Monthly",
|
|
title: "Monthly",
|
|
|
price: paywallDisplayPrice(for: .monthly),
|
|
price: paywallDisplayPrice(for: .monthly),
|
|
|
- badge: "Free Trial",
|
|
|
|
|
|
|
+ badge: "Popular",
|
|
|
badgeColor: NSColor(calibratedRed: 0.19, green: 0.82, blue: 0.39, alpha: 1),
|
|
badgeColor: NSColor(calibratedRed: 0.19, green: 0.82, blue: 0.39, alpha: 1),
|
|
|
- subtitle: "7-day free trial, then monthly",
|
|
|
|
|
|
|
+ subtitle: "Billed monthly",
|
|
|
plan: .monthly,
|
|
plan: .monthly,
|
|
|
strikePrice: nil
|
|
strikePrice: nil
|
|
|
)
|
|
)
|
|
|
let yearlyCard = paywallPlanCard(
|
|
let yearlyCard = paywallPlanCard(
|
|
|
title: "Yearly",
|
|
title: "Yearly",
|
|
|
price: paywallDisplayPrice(for: .yearly),
|
|
price: paywallDisplayPrice(for: .yearly),
|
|
|
- badge: "Best Deal",
|
|
|
|
|
|
|
+ badge: "Save 67%",
|
|
|
badgeColor: NSColor(calibratedRed: 1.0, green: 0.60, blue: 0.20, alpha: 1),
|
|
badgeColor: NSColor(calibratedRed: 1.0, green: 0.60, blue: 0.20, alpha: 1),
|
|
|
- subtitle: "Billed yearly",
|
|
|
|
|
|
|
+ subtitle: "Billed yearly • 3 days free trial",
|
|
|
plan: .yearly,
|
|
plan: .yearly,
|
|
|
strikePrice: nil
|
|
strikePrice: nil
|
|
|
)
|
|
)
|
|
|
let lifetimeCard = paywallPlanCard(
|
|
let lifetimeCard = paywallPlanCard(
|
|
|
title: "Lifetime",
|
|
title: "Lifetime",
|
|
|
price: paywallDisplayPrice(for: .lifetime),
|
|
price: paywallDisplayPrice(for: .lifetime),
|
|
|
- badge: "Save 50%",
|
|
|
|
|
|
|
+ badge: "Best Value",
|
|
|
badgeColor: NSColor(calibratedRed: 1.0, green: 0.60, blue: 0.20, alpha: 1),
|
|
badgeColor: NSColor(calibratedRed: 1.0, green: 0.60, blue: 0.20, alpha: 1),
|
|
|
subtitle: "One-time purchase",
|
|
subtitle: "One-time purchase",
|
|
|
plan: .lifetime,
|
|
plan: .lifetime,
|
|
@@ -3822,6 +3827,7 @@ class ViewController: NSViewController {
|
|
|
plansRowWide.addArrangedSubview(lifetimeCard)
|
|
plansRowWide.addArrangedSubview(lifetimeCard)
|
|
|
paywallPlansRowWide = plansRowWide
|
|
paywallPlansRowWide = plansRowWide
|
|
|
planColumn.addArrangedSubview(plansRowWide)
|
|
planColumn.addArrangedSubview(plansRowWide)
|
|
|
|
|
+ plansRowWide.widthAnchor.constraint(equalTo: planColumn.widthAnchor).isActive = true
|
|
|
|
|
|
|
|
let plansGridNarrow = NSStackView()
|
|
let plansGridNarrow = NSStackView()
|
|
|
plansGridNarrow.translatesAutoresizingMaskIntoConstraints = false
|
|
plansGridNarrow.translatesAutoresizingMaskIntoConstraints = false
|
|
@@ -3885,6 +3891,9 @@ class ViewController: NSViewController {
|
|
|
plansGridNarrow.isHidden = true
|
|
plansGridNarrow.isHidden = true
|
|
|
paywallPlansGridNarrow = plansGridNarrow
|
|
paywallPlansGridNarrow = plansGridNarrow
|
|
|
planColumn.addArrangedSubview(plansGridNarrow)
|
|
planColumn.addArrangedSubview(plansGridNarrow)
|
|
|
|
|
+ plansGridNarrow.widthAnchor.constraint(equalTo: planColumn.widthAnchor).isActive = true
|
|
|
|
|
+ plansRow1.widthAnchor.constraint(equalTo: plansGridNarrow.widthAnchor).isActive = true
|
|
|
|
|
+ plansRow2.widthAnchor.constraint(equalTo: plansGridNarrow.widthAnchor).isActive = true
|
|
|
|
|
|
|
|
updatePaywallPlanSelection()
|
|
updatePaywallPlanSelection()
|
|
|
planColumn.setCustomSpacing(16, after: plansGridNarrow)
|
|
planColumn.setCustomSpacing(16, after: plansGridNarrow)
|
|
@@ -3896,16 +3905,17 @@ class ViewController: NSViewController {
|
|
|
offerWrap.translatesAutoresizingMaskIntoConstraints = false
|
|
offerWrap.translatesAutoresizingMaskIntoConstraints = false
|
|
|
offerWrap.addSubview(offer)
|
|
offerWrap.addSubview(offer)
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
- offerWrap.widthAnchor.constraint(greaterThanOrEqualToConstant: paywallContentWidth),
|
|
|
|
|
offer.centerXAnchor.constraint(equalTo: offerWrap.centerXAnchor),
|
|
offer.centerXAnchor.constraint(equalTo: offerWrap.centerXAnchor),
|
|
|
offer.topAnchor.constraint(equalTo: offerWrap.topAnchor, constant: 6),
|
|
offer.topAnchor.constraint(equalTo: offerWrap.topAnchor, constant: 6),
|
|
|
offer.bottomAnchor.constraint(equalTo: offerWrap.bottomAnchor, constant: -2)
|
|
offer.bottomAnchor.constraint(equalTo: offerWrap.bottomAnchor, constant: -2)
|
|
|
])
|
|
])
|
|
|
planColumn.addArrangedSubview(offerWrap)
|
|
planColumn.addArrangedSubview(offerWrap)
|
|
|
|
|
+ offerWrap.widthAnchor.constraint(equalTo: planColumn.widthAnchor).isActive = true
|
|
|
planColumn.setCustomSpacing(14, after: offerWrap)
|
|
planColumn.setCustomSpacing(14, after: offerWrap)
|
|
|
|
|
|
|
|
let trustRow = paywallTrustRow()
|
|
let trustRow = paywallTrustRow()
|
|
|
planColumn.addArrangedSubview(trustRow)
|
|
planColumn.addArrangedSubview(trustRow)
|
|
|
|
|
+ trustRow.widthAnchor.constraint(equalTo: planColumn.widthAnchor).isActive = true
|
|
|
planColumn.setCustomSpacing(14, after: trustRow)
|
|
planColumn.setCustomSpacing(14, after: trustRow)
|
|
|
|
|
|
|
|
let continueButton = HoverButton(title: "", target: self, action: #selector(paywallContinueClicked(_:)))
|
|
let continueButton = HoverButton(title: "", target: self, action: #selector(paywallContinueClicked(_:)))
|
|
@@ -3914,10 +3924,9 @@ class ViewController: NSViewController {
|
|
|
continueButton.bezelStyle = .regularSquare
|
|
continueButton.bezelStyle = .regularSquare
|
|
|
continueButton.wantsLayer = true
|
|
continueButton.wantsLayer = true
|
|
|
continueButton.layer?.cornerRadius = 14
|
|
continueButton.layer?.cornerRadius = 14
|
|
|
- continueButton.normalColor = NSColor(calibratedRed: 0.0, green: 0.70, blue: 0.42, alpha: 1)
|
|
|
|
|
- continueButton.hoverColor = NSColor(calibratedRed: 0.0, green: 0.70, blue: 0.42, alpha: 0.92)
|
|
|
|
|
|
|
+ continueButton.normalColor = NSColor(calibratedRed: 0.16, green: 0.49, blue: 0.98, alpha: 1)
|
|
|
|
|
+ continueButton.hoverColor = NSColor(calibratedRed: 0.16, green: 0.49, blue: 0.98, alpha: 0.92)
|
|
|
continueButton.heightAnchor.constraint(equalToConstant: 44).isActive = true
|
|
continueButton.heightAnchor.constraint(equalToConstant: 44).isActive = true
|
|
|
- continueButton.widthAnchor.constraint(greaterThanOrEqualToConstant: paywallContentWidth).isActive = true
|
|
|
|
|
styleSurface(continueButton, borderColor: NSColor.black.withAlphaComponent(0.12), borderWidth: 1, shadow: true)
|
|
styleSurface(continueButton, borderColor: NSColor.black.withAlphaComponent(0.12), borderWidth: 1, shadow: true)
|
|
|
let continueLabel = textLabel("Continue", font: NSFont.systemFont(ofSize: 16, weight: .bold), color: .white)
|
|
let continueLabel = textLabel("Continue", font: NSFont.systemFont(ofSize: 16, weight: .bold), color: .white)
|
|
|
continueButton.addSubview(continueLabel)
|
|
continueButton.addSubview(continueLabel)
|
|
@@ -3928,6 +3937,7 @@ class ViewController: NSViewController {
|
|
|
paywallContinueButton = continueButton
|
|
paywallContinueButton = continueButton
|
|
|
paywallContinueLabel = continueLabel
|
|
paywallContinueLabel = continueLabel
|
|
|
planColumn.addArrangedSubview(continueButton)
|
|
planColumn.addArrangedSubview(continueButton)
|
|
|
|
|
+ continueButton.widthAnchor.constraint(equalTo: planColumn.widthAnchor).isActive = true
|
|
|
planColumn.setCustomSpacing(16, after: continueButton)
|
|
planColumn.setCustomSpacing(16, after: continueButton)
|
|
|
|
|
|
|
|
let secure = textLabel("Secured by Apple. Cancel anytime.", font: NSFont.systemFont(ofSize: 12, weight: .semibold), color: secondaryText)
|
|
let secure = textLabel("Secured by Apple. Cancel anytime.", font: NSFont.systemFont(ofSize: 12, weight: .semibold), color: secondaryText)
|
|
@@ -3936,28 +3946,14 @@ class ViewController: NSViewController {
|
|
|
secureWrap.translatesAutoresizingMaskIntoConstraints = false
|
|
secureWrap.translatesAutoresizingMaskIntoConstraints = false
|
|
|
secureWrap.addSubview(secure)
|
|
secureWrap.addSubview(secure)
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
- secureWrap.widthAnchor.constraint(greaterThanOrEqualToConstant: paywallContentWidth),
|
|
|
|
|
secure.centerXAnchor.constraint(equalTo: secureWrap.centerXAnchor),
|
|
secure.centerXAnchor.constraint(equalTo: secureWrap.centerXAnchor),
|
|
|
secure.topAnchor.constraint(equalTo: secureWrap.topAnchor, constant: 4),
|
|
secure.topAnchor.constraint(equalTo: secureWrap.topAnchor, constant: 4),
|
|
|
secure.bottomAnchor.constraint(equalTo: secureWrap.bottomAnchor, constant: -8)
|
|
secure.bottomAnchor.constraint(equalTo: secureWrap.bottomAnchor, constant: -8)
|
|
|
])
|
|
])
|
|
|
planColumn.addArrangedSubview(secureWrap)
|
|
planColumn.addArrangedSubview(secureWrap)
|
|
|
|
|
+ secureWrap.widthAnchor.constraint(equalTo: planColumn.widthAnchor).isActive = true
|
|
|
planColumn.setCustomSpacing(8, after: secureWrap)
|
|
planColumn.setCustomSpacing(8, after: secureWrap)
|
|
|
|
|
|
|
|
- let footerButtons = NSStackView()
|
|
|
|
|
- footerButtons.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
- footerButtons.orientation = .horizontal
|
|
|
|
|
- footerButtons.alignment = .centerY
|
|
|
|
|
- footerButtons.distribution = .fill
|
|
|
|
|
- footerButtons.spacing = 8
|
|
|
|
|
- footerButtons.widthAnchor.constraint(greaterThanOrEqualToConstant: paywallContentWidth).isActive = true
|
|
|
|
|
-
|
|
|
|
|
- let leftFooter = NSStackView()
|
|
|
|
|
- leftFooter.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
- leftFooter.orientation = .horizontal
|
|
|
|
|
- leftFooter.alignment = .centerY
|
|
|
|
|
- leftFooter.spacing = 14
|
|
|
|
|
-
|
|
|
|
|
let manageButton = paywallFooterButton(
|
|
let manageButton = paywallFooterButton(
|
|
|
title: "Manage Subscription",
|
|
title: "Manage Subscription",
|
|
|
action: #selector(paywallManageSubscriptionClicked(_:))
|
|
action: #selector(paywallManageSubscriptionClicked(_:))
|
|
@@ -3966,12 +3962,11 @@ class ViewController: NSViewController {
|
|
|
title: "Restore Purchase",
|
|
title: "Restore Purchase",
|
|
|
action: #selector(paywallRestorePurchaseClicked(_:))
|
|
action: #selector(paywallRestorePurchaseClicked(_:))
|
|
|
)
|
|
)
|
|
|
- leftFooter.addArrangedSubview(manageButton)
|
|
|
|
|
- leftFooter.addArrangedSubview(restoreButton)
|
|
|
|
|
-
|
|
|
|
|
- let footerSpacer = NSView()
|
|
|
|
|
- footerSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
-
|
|
|
|
|
|
|
+ let freePlanButton = paywallFooterButton(
|
|
|
|
|
+ title: "Continue with free plan",
|
|
|
|
|
+ action: #selector(closePaywallClicked(_:))
|
|
|
|
|
+ )
|
|
|
|
|
+ freePlanButton.contentTintColor = secondaryText
|
|
|
let privacyButton = paywallFooterButton(
|
|
let privacyButton = paywallFooterButton(
|
|
|
title: "Privacy Policy",
|
|
title: "Privacy Policy",
|
|
|
action: #selector(paywallPrivacyPolicyClicked(_:))
|
|
action: #selector(paywallPrivacyPolicyClicked(_:))
|
|
@@ -3984,18 +3979,59 @@ class ViewController: NSViewController {
|
|
|
title: "Terms of Services",
|
|
title: "Terms of Services",
|
|
|
action: #selector(paywallTermsOfServicesClicked(_:))
|
|
action: #selector(paywallTermsOfServicesClicked(_:))
|
|
|
)
|
|
)
|
|
|
- footerButtons.addArrangedSubview(leftFooter)
|
|
|
|
|
- footerButtons.addArrangedSubview(footerSpacer)
|
|
|
|
|
- footerButtons.addArrangedSubview(privacyButton)
|
|
|
|
|
- footerButtons.addArrangedSubview(supportButton)
|
|
|
|
|
- footerButtons.addArrangedSubview(termsButton)
|
|
|
|
|
- planColumn.addArrangedSubview(footerButtons)
|
|
|
|
|
|
|
|
|
|
|
|
+ // Footer: single centered line.
|
|
|
|
|
+ let footerContent = NSStackView()
|
|
|
|
|
+ footerContent.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ footerContent.orientation = .horizontal
|
|
|
|
|
+ footerContent.alignment = .centerY
|
|
|
|
|
+ footerContent.distribution = .fill
|
|
|
|
|
+ footerContent.spacing = 16
|
|
|
|
|
+ footerContent.addArrangedSubview(manageButton)
|
|
|
|
|
+ footerContent.addArrangedSubview(restoreButton)
|
|
|
|
|
+ footerContent.addArrangedSubview(freePlanButton)
|
|
|
|
|
+ footerContent.addArrangedSubview(privacyButton)
|
|
|
|
|
+ footerContent.addArrangedSubview(supportButton)
|
|
|
|
|
+ footerContent.addArrangedSubview(termsButton)
|
|
|
|
|
+
|
|
|
|
|
+ let footerWrap = NSStackView()
|
|
|
|
|
+ footerWrap.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ footerWrap.orientation = .horizontal
|
|
|
|
|
+ footerWrap.alignment = .centerY
|
|
|
|
|
+ footerWrap.distribution = .fill
|
|
|
|
|
+
|
|
|
|
|
+ let footerLeftSpacer = NSView()
|
|
|
|
|
+ footerLeftSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ let footerRightSpacer = NSView()
|
|
|
|
|
+ footerRightSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ footerLeftSpacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ footerLeftSpacer.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ footerRightSpacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ footerRightSpacer.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ footerContent.setContentHuggingPriority(.required, for: .horizontal)
|
|
|
|
|
+ footerContent.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
|
|
|
+
|
|
|
|
|
+ footerWrap.addArrangedSubview(footerLeftSpacer)
|
|
|
|
|
+ footerWrap.addArrangedSubview(footerContent)
|
|
|
|
|
+ footerWrap.addArrangedSubview(footerRightSpacer)
|
|
|
|
|
+
|
|
|
|
|
+ // Push footer to the bottom of the paywall.
|
|
|
|
|
+ let footerPushSpacer = NSView()
|
|
|
|
|
+ footerPushSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ footerPushSpacer.setContentHuggingPriority(.defaultLow, for: .vertical)
|
|
|
|
|
+ footerPushSpacer.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
|
|
|
|
|
+ planColumn.addArrangedSubview(footerPushSpacer)
|
|
|
|
|
+ planColumn.addArrangedSubview(footerWrap)
|
|
|
|
|
+ footerWrap.widthAnchor.constraint(equalTo: planColumn.widthAnchor).isActive = true
|
|
|
|
|
+
|
|
|
|
|
+ let contentBottomEq = contentStack.bottomAnchor.constraint(equalTo: panel.bottomAnchor, constant: -18)
|
|
|
|
|
+ contentBottomEq.priority = .defaultLow
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
- contentStack.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 18),
|
|
|
|
|
- contentStack.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -18),
|
|
|
|
|
- contentStack.topAnchor.constraint(equalTo: panel.topAnchor, constant: 16),
|
|
|
|
|
- contentStack.bottomAnchor.constraint(lessThanOrEqualTo: panel.bottomAnchor, constant: -16)
|
|
|
|
|
|
|
+ contentStack.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 22),
|
|
|
|
|
+ contentStack.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -22),
|
|
|
|
|
+ contentStack.topAnchor.constraint(equalTo: panel.topAnchor, constant: 56),
|
|
|
|
|
+ contentStack.bottomAnchor.constraint(lessThanOrEqualTo: panel.bottomAnchor, constant: -18),
|
|
|
|
|
+ contentBottomEq
|
|
|
])
|
|
])
|
|
|
|
|
|
|
|
refreshPaywallStoreUI()
|
|
refreshPaywallStoreUI()
|
|
@@ -4081,8 +4117,8 @@ class ViewController: NSViewController {
|
|
|
row.alignment = .centerY
|
|
row.alignment = .centerY
|
|
|
row.distribution = .fillEqually
|
|
row.distribution = .fillEqually
|
|
|
|
|
|
|
|
- row.addArrangedSubview(paywallBenefitItem(icon: "📅", text: "Manage classes"))
|
|
|
|
|
- row.addArrangedSubview(paywallBenefitItem(icon: "💬", text: "Smart to-dos"))
|
|
|
|
|
|
|
+ row.addArrangedSubview(paywallBenefitItem(icon: "📅", text: "Manage meetings"))
|
|
|
|
|
+ row.addArrangedSubview(paywallBenefitItem(icon: "💬", text: "Smart scheduling"))
|
|
|
row.addArrangedSubview(paywallBenefitItem(icon: "⚡", text: "Faster workflow"))
|
|
row.addArrangedSubview(paywallBenefitItem(icon: "⚡", text: "Faster workflow"))
|
|
|
row.addArrangedSubview(paywallBenefitItem(icon: "🔔", text: "Reminder notifications"))
|
|
row.addArrangedSubview(paywallBenefitItem(icon: "🔔", text: "Reminder notifications"))
|
|
|
return row
|
|
return row
|
|
@@ -4183,7 +4219,7 @@ class ViewController: NSViewController {
|
|
|
wrapper.bezelStyle = .regularSquare
|
|
wrapper.bezelStyle = .regularSquare
|
|
|
wrapper.wantsLayer = true
|
|
wrapper.wantsLayer = true
|
|
|
wrapper.layer?.backgroundColor = NSColor.clear.cgColor
|
|
wrapper.layer?.backgroundColor = NSColor.clear.cgColor
|
|
|
- wrapper.heightAnchor.constraint(equalToConstant: 122).isActive = true
|
|
|
|
|
|
|
+ wrapper.heightAnchor.constraint(equalToConstant: 138).isActive = true
|
|
|
wrapper.tag = PremiumPlan.allCases.firstIndex(of: plan) ?? 0
|
|
wrapper.tag = PremiumPlan.allCases.firstIndex(of: plan) ?? 0
|
|
|
|
|
|
|
|
let card = NSView()
|
|
let card = NSView()
|
|
@@ -4195,7 +4231,7 @@ class ViewController: NSViewController {
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
card.leadingAnchor.constraint(equalTo: wrapper.leadingAnchor),
|
|
card.leadingAnchor.constraint(equalTo: wrapper.leadingAnchor),
|
|
|
card.trailingAnchor.constraint(equalTo: wrapper.trailingAnchor),
|
|
card.trailingAnchor.constraint(equalTo: wrapper.trailingAnchor),
|
|
|
- card.topAnchor.constraint(equalTo: wrapper.topAnchor, constant: 12),
|
|
|
|
|
|
|
+ card.topAnchor.constraint(equalTo: wrapper.topAnchor, constant: 14),
|
|
|
card.bottomAnchor.constraint(equalTo: wrapper.bottomAnchor)
|
|
card.bottomAnchor.constraint(equalTo: wrapper.bottomAnchor)
|
|
|
])
|
|
])
|
|
|
styleSurface(card, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
styleSurface(card, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
@@ -4288,8 +4324,8 @@ class ViewController: NSViewController {
|
|
|
let price = paywallDisplayPrice(for: plan)
|
|
let price = paywallDisplayPrice(for: plan)
|
|
|
switch plan {
|
|
switch plan {
|
|
|
case .weekly: return "\(price)/week"
|
|
case .weekly: return "\(price)/week"
|
|
|
- case .monthly: return "7-day free trial, then \(price)/month"
|
|
|
|
|
- case .yearly: return "\(price)/year"
|
|
|
|
|
|
|
+ case .monthly: return "\(price)/month"
|
|
|
|
|
+ case .yearly: return "Free for 3 days, then \(price)/year"
|
|
|
case .lifetime: return "\(price) one-time purchase"
|
|
case .lifetime: return "\(price) one-time purchase"
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|