Merge branch 'describe-purchase-extent'

This commit is contained in:
Davide De Rosa 2019-11-09 18:08:44 +01:00
commit 6a52cb313f
7 changed files with 93 additions and 24 deletions

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="fEC-GT-W4O">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="fEC-GT-W4O">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@ -27,29 +27,36 @@
<scene sceneID="93l-dg-vRI">
<objects>
<tableViewController id="bQc-2A-qWz" customClass="PurchaseViewController" customModule="Passepartout" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="5WE-4Q-uDQ">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="5WE-4Q-uDQ">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="PurchaseTableViewCell" id="0XE-hK-4Ro" customClass="PurchaseTableViewCell" customModule="Passepartout" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="414" height="105"/>
<rect key="frame" x="0.0" y="55.5" width="414" height="99"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="0XE-hK-4Ro" id="Fe6-eH-sBT">
<rect key="frame" x="0.0" y="0.0" width="414" height="105"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="99"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Dit-R5-2h7">
<rect key="frame" x="20" y="20" width="374" height="65"/>
<rect key="frame" x="20" y="20" width="374" height="59"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Hf0-aN-JRs">
<rect key="frame" x="0.0" y="0.0" width="374" height="24.5"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="20"/>
<rect key="frame" x="0.0" y="0.0" width="34.5" height="21"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Price" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="892-go-vOV">
<rect key="frame" x="334" y="0.0" width="40" height="20.5"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Description" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gB1-nL-LEc">
<rect key="frame" x="0.0" y="44.5" width="374" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<rect key="frame" x="0.0" y="41" width="374" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
@ -58,10 +65,12 @@
<constraint firstAttribute="trailing" secondItem="gB1-nL-LEc" secondAttribute="trailing" id="062-HW-sKW"/>
<constraint firstItem="gB1-nL-LEc" firstAttribute="leading" secondItem="Dit-R5-2h7" secondAttribute="leading" id="3je-C8-e0v"/>
<constraint firstItem="Hf0-aN-JRs" firstAttribute="leading" secondItem="Dit-R5-2h7" secondAttribute="leading" id="7cX-Z8-Z9p"/>
<constraint firstAttribute="trailing" secondItem="892-go-vOV" secondAttribute="trailing" id="Dt7-md-WxN"/>
<constraint firstItem="Hf0-aN-JRs" firstAttribute="top" secondItem="Dit-R5-2h7" secondAttribute="top" id="FOS-1V-gEv"/>
<constraint firstAttribute="trailing" secondItem="Hf0-aN-JRs" secondAttribute="trailing" id="MQl-hH-lvW"/>
<constraint firstItem="892-go-vOV" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Hf0-aN-JRs" secondAttribute="trailing" constant="20" id="P3K-6q-k6r"/>
<constraint firstItem="gB1-nL-LEc" firstAttribute="top" secondItem="Hf0-aN-JRs" secondAttribute="bottom" constant="20" id="dKT-d6-56g"/>
<constraint firstAttribute="bottom" secondItem="gB1-nL-LEc" secondAttribute="bottom" id="h1s-nk-2kw"/>
<constraint firstItem="892-go-vOV" firstAttribute="top" secondItem="Dit-R5-2h7" secondAttribute="top" id="tzq-5O-Ntg"/>
</constraints>
</view>
</subviews>
@ -74,6 +83,7 @@
</tableViewCellContentView>
<connections>
<outlet property="labelDescription" destination="gB1-nL-LEc" id="zIn-g5-Rl0"/>
<outlet property="labelPrice" destination="892-go-vOV" id="9Kf-XF-zUy"/>
<outlet property="labelTitle" destination="Hf0-aN-JRs" id="E65-5C-zZR"/>
</connections>
</tableViewCell>

View File

@ -97,6 +97,18 @@ enum Product: String {
// MARK: All
static let all: [Product] = allDonations + allFeatures + allProviders
var isDonation: Bool {
return Product.allDonations.contains(self)
}
var isFeature: Bool {
return Product.allFeatures.contains(self)
}
var isProvider: Bool {
return Product.allProviders.contains(self)
}
}
extension Infrastructure.Name {

View File

@ -80,6 +80,21 @@ class ProductManager: NSObject {
return inApp.product(withIdentifier: identifier)
}
func featureProducts(includingFullVersion: Bool) -> [SKProduct] {
return inApp.products.filter {
guard let p = Product(rawValue: $0.productIdentifier) else {
return false
}
guard includingFullVersion || p != .fullVersion else {
return false
}
guard p.isFeature else {
return false
}
return true
}
}
func purchase(_ product: SKProduct, completionHandler: @escaping (InAppPurchaseResult, Error?) -> Void) {
inApp.purchase(product: product) {
if $0 == .success {

View File

@ -82,6 +82,12 @@ internal enum L10n {
/// Purchase
internal static let title = L10n.tr("App", "purchase.title")
internal enum Cells {
internal enum FullVersion {
/// \n- All providers (including those being added in the future)\n%@
internal static func extraDescription(_ p1: String) -> String {
return L10n.tr("App", "purchase.cells.full_version.extra_description", p1)
}
}
internal enum Restore {
/// If you bought this app or feature in the past, you can restore your purchases and this screen won't show again.
internal static let description = L10n.tr("App", "purchase.cells.restore.description")
@ -89,6 +95,12 @@ internal enum L10n {
internal static let title = L10n.tr("App", "purchase.cells.restore.title")
}
}
internal enum Sections {
internal enum Products {
/// Every product is a one-time purchase.
internal static let footer = L10n.tr("App", "purchase.sections.products.footer")
}
}
}
internal enum Service {
internal enum Alerts {

View File

@ -61,5 +61,7 @@
"shortcuts.edit.cells.add_shortcut.caption" = "Add shortcut";
"purchase.title" = "Purchase";
"purchase.sections.products.footer" = "Every product is a one-time purchase.";
"purchase.cells.full_version.extra_description" = "- All providers (including those being added in the future)\n%@";
"purchase.cells.restore.title" = "Restore purchases";
"purchase.cells.restore.description" = "If you bought this app or feature in the past, you can restore your purchases and this screen won't show again.";

View File

@ -29,27 +29,28 @@ import StoreKit
class PurchaseTableViewCell: UITableViewCell {
@IBOutlet private weak var labelTitle: UILabel?
@IBOutlet private weak var labelPrice: UILabel?
@IBOutlet private weak var labelDescription: UILabel?
override func awakeFromNib() {
super.awakeFromNib()
labelTitle?.applyAccent(.current)
labelPrice?.applyAccent(.current)
}
func fill(product: SKProduct) {
var title = product.localizedTitle
if let price = product.localizedPrice {
title += " @ \(price)"
}
func fill(product: SKProduct, customDescription: String? = nil) {
fill(
title: title,
description: "\(product.localizedDescription)."
title: product.localizedTitle,
description: customDescription ?? "\(product.localizedDescription)."
)
labelPrice?.text = product.localizedPrice
}
func fill(title: String, description: String) {
labelTitle?.text = title
labelDescription?.text = description
labelPrice?.text = nil
}
}

View File

@ -38,6 +38,8 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
private var skFeature: SKProduct?
private var skFullVersion: SKProduct?
private var fullVersionExtra: String?
// MARK: StrongTableHost
@ -46,19 +48,26 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
func reloadModel() {
model.clear()
model.add(.products)
model.setFooter(L10n.App.Purchase.Sections.Products.footer, forSection: .products)
var rows: [RowType] = []
let pm = ProductManager.shared
if let skFeature = pm.product(withIdentifier: feature) {
self.skFeature = skFeature
rows.append(.feature)
}
if let skFullVersion = pm.product(withIdentifier: .fullVersion) {
self.skFullVersion = skFullVersion
rows.append(.fullVersion)
}
if let skFeature = pm.product(withIdentifier: feature) {
self.skFeature = skFeature
rows.append(.feature)
}
rows.append(.restore)
model.set(rows, forSection: .products)
let featureBulletsList: [String] = ProductManager.shared.featureProducts(includingFullVersion: false).map {
return "- \($0.localizedTitle)"
}.sortedCaseInsensitive()
let featureBullets = featureBulletsList.joined(separator: "\n")
fullVersionExtra = L10n.App.Purchase.Cells.FullVersion.extraDescription(featureBullets)
}
// MARK: UIViewController
@ -156,6 +165,14 @@ extension PurchaseViewController {
case restore
}
override func numberOfSections(in tableView: UITableView) -> Int {
return model.numberOfSections
}
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return model.footer(forSection: section)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard !isLoading else {
return 0
@ -176,7 +193,7 @@ extension PurchaseViewController {
guard let product = skFullVersion else {
fatalError("Loaded full version cell, yet no corresponding product?")
}
cell.fill(product: product)
cell.fill(product: product, customDescription: fullVersionExtra)
case .restore:
cell.fill(