Skip to content

Commit 53986ec

Browse files
committed
Merge branch 'develop' into issue/2053-add-product-more-details-button
# Conflicts: # WooCommerce/WooCommerce.xcodeproj/project.pbxproj
2 parents 6cdc204 + e6efb0d commit 53986ec

26 files changed

+555
-92
lines changed

Networking/Networking/Model/Product/Product.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ public struct Product: Codable {
461461
try container.encode(catalogVisibilityKey, forKey: .catalogVisibilityKey)
462462
try container.encode(slug, forKey: .slug)
463463
try container.encode(purchaseNote, forKey: .purchaseNote)
464+
try container.encode(menuOrder, forKey: .menuOrder)
464465
}
465466
}
466467

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- [internal]: the "send magic link" screen has navigation changes that can cause regressions. See https://git.io/Jfqio for testing details.
88
- The Orders list is now automatically refreshed when reopening the app.
99
- The Orders list is automatically refreshed if a new order (push notification) comes in.
10+
- Orders -> Search: The statuses now shows the total number of orders with that status.
1011

1112

1213
4.1

WooCommerce/Classes/Authentication/Prologue/LoginPrologueViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ private extension LoginPrologueViewController {
112112
}
113113

114114
func setupLoginButton() {
115-
let title = NSLocalizedString("Log in with Jetpack", comment: "Authentication Login Button")
115+
let title = NSLocalizedString("Log In", comment: "Authentication Login Button")
116116
loginButton.titleLabel?.adjustsFontForContentSizeCategory = true
117117
loginButton.setTitle(title, for: .normal)
118118
loginButton.accessibilityIdentifier = "login-prologue-login-with-jetpack"

WooCommerce/Classes/Authentication/Prologue/LoginPrologueViewController.xib

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
33
<device id="retina6_1" orientation="portrait" appearance="light"/>
44
<dependencies>
55
<deployment identifier="iOS"/>
6-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/>
77
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
88
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
99
</dependencies>
@@ -59,7 +59,7 @@ L3</string>
5959
<constraints>
6060
<constraint firstAttribute="height" constant="50" id="Np7-w5-zh4"/>
6161
</constraints>
62-
<state key="normal" title="Log in with Jetpack"/>
62+
<state key="normal" title="Log In"/>
6363
<connections>
6464
<action selector="loginWasPressed" destination="-1" eventType="touchUpInside" id="TN0-aw-ptZ"/>
6565
</connections>
@@ -79,10 +79,10 @@ L3</string>
7979
</constraints>
8080
</view>
8181
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1" image="prologue-slanted-rectangle" translatesAutoresizingMaskIntoConstraints="NO" id="Rhn-8s-tRs" userLabel="Slanted ImageView">
82-
<rect key="frame" x="0.0" y="365.5" width="414" height="320"/>
82+
<rect key="frame" x="0.0" y="525.5" width="414" height="160"/>
8383
</imageView>
8484
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="1000" verticalCompressionResistancePriority="200" image="prologue-bubbles" translatesAutoresizingMaskIntoConstraints="NO" id="pzF-Fz-byr" userLabel="Bubbles View">
85-
<rect key="frame" x="158" y="273" width="216" height="350"/>
85+
<rect key="frame" x="266" y="360.5" width="108" height="175"/>
8686
<constraints>
8787
<constraint firstAttribute="width" secondItem="pzF-Fz-byr" secondAttribute="height" multiplier="108:175" id="Rsb-pu-GHs"/>
8888
</constraints>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
import Foundation
3+
4+
extension NumberFormatter {
5+
/// Returns `number` as a localized string or “99+” if it is greater than `99`.
6+
///
7+
static func localizedOrNinetyNinePlus(_ number: Int) -> String {
8+
if number > 99 {
9+
return Constants.ninetyNinePlus
10+
} else {
11+
return localizedString(from: NSNumber(value: number), number: .none)
12+
}
13+
}
14+
15+
private enum Constants {
16+
static let ninetyNinePlus = NSLocalizedString(
17+
"99+",
18+
comment: "Shown when there are more than 99 items of something (e.g. Processing Orders)."
19+
)
20+
}
21+
}
Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,44 @@
11
import Foundation
22

3-
/// `UnitInputFormatter` implementation for integer number input.
3+
/// `UnitInputFormatter` implementation for integer number input (positive, negative, or zero)
44
///
55
struct IntegerInputFormatter: UnitInputFormatter {
6+
7+
/// The default value used by format() when the text is empty or the number formatter returns a nil value
8+
///
9+
let defaultValue: String
10+
11+
private let minus: Character = "-"
12+
13+
private let numberFormatter: NumberFormatter = {
14+
let numberFormatter = NumberFormatter()
15+
numberFormatter.allowsFloats = false
16+
return numberFormatter
17+
}()
18+
619
func isValid(input: String) -> Bool {
720
guard input.isEmpty == false else {
8-
// Allows empty input to be replaced by 0.
21+
// Allows empty input to be replaced by defaultValue
922
return true
1023
}
11-
return Int(input) != nil
24+
return numberFormatter.number(from: input) != nil || input == String(minus)
1225
}
1326

1427
func format(input text: String?) -> String {
1528
guard let text = text, text.isEmpty == false else {
16-
return "0"
29+
return defaultValue
1730
}
1831

19-
let formattedText = text
20-
// Removes leading 0s before the digits.
21-
.replacingOccurrences(of: "^0+([1-9]+)", with: "$1", options: .regularExpression)
22-
// Maximum one leading 0.
23-
.replacingOccurrences(of: "^(0+)", with: "0", options: .regularExpression)
32+
var formattedText = numberFormatter.number(from: text)?.stringValue ?? defaultValue
33+
34+
// The minus sign is maintained if present
35+
if text.first == minus {
36+
formattedText = String(minus) + formattedText.replacingOccurrences(of: "-", with: "")
37+
}
2438
return formattedText
2539
}
40+
41+
init(defaultValue: String = "0") {
42+
self.defaultValue = defaultValue
43+
}
2644
}

WooCommerce/Classes/ViewModels/MainTabViewModel.swift

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,6 @@ final class MainTabViewModel {
2929

3030

3131
private extension MainTabViewModel {
32-
enum Constants {
33-
static let ninetyNinePlus = NSLocalizedString(
34-
"99+",
35-
comment: "Content of the badge presented over the Orders icon when there are more than 99 orders processing"
36-
)
37-
}
38-
3932
@objc func requestBadgeCount() {
4033
guard let siteID = ServiceLocator.stores.sessionManager.defaultStoreID else {
4134
DDLogError("# Error: Cannot fetch order count")
@@ -62,14 +55,7 @@ private extension MainTabViewModel {
6255
return
6356
}
6457

65-
let returnValue = readableCount(processingCount)
66-
67-
onBadgeReload?(returnValue)
68-
}
69-
70-
private func readableCount(_ count: Int) -> String {
71-
let localizedCount = NumberFormatter.localizedString(from: NSNumber(value: count), number: .none)
72-
return count > 99 ? Constants.ninetyNinePlus : localizedCount
58+
onBadgeReload?(NumberFormatter.localizedOrNinetyNinePlus(processingCount))
7359
}
7460

7561
private func observeBadgeRefreshNotifications() {
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import UIKit
2+
import Yosemite
3+
4+
final class ProductMenuOrderViewController: UIViewController {
5+
6+
@IBOutlet weak private var tableView: UITableView!
7+
8+
// Completion callback
9+
//
10+
typealias Completion = (_ productSettings: ProductSettings) -> Void
11+
private let onCompletion: Completion
12+
13+
private let productSettings: ProductSettings
14+
15+
private let sections: [Section]
16+
17+
/// Init
18+
///
19+
init(settings: ProductSettings, completion: @escaping Completion) {
20+
productSettings = settings
21+
let footerText = NSLocalizedString("Determines the products positioning in the catalog."
22+
+ " The lower the value of the number, the higher the item will be on the product list. You can also use negative values",
23+
comment: "Footer text in Product Menu order screen")
24+
sections = [Section(footer: footerText, rows: [.menuOrder])]
25+
onCompletion = completion
26+
super.init(nibName: nil, bundle: nil)
27+
}
28+
29+
required init?(coder: NSCoder) {
30+
fatalError("init(coder:) has not been implemented")
31+
}
32+
33+
override func viewDidLoad() {
34+
super.viewDidLoad()
35+
configureNavigationBar()
36+
configureMainView()
37+
configureTableView()
38+
}
39+
40+
override func viewWillDisappear(_ animated: Bool) {
41+
super.viewWillDisappear(animated)
42+
if isMovingFromParent {
43+
onCompletion(productSettings)
44+
}
45+
}
46+
47+
override func viewDidAppear(_ animated: Bool) {
48+
super.viewDidAppear(animated)
49+
configureFirstTextFieldAsFirstResponder()
50+
}
51+
}
52+
53+
// MARK: - View Configuration
54+
//
55+
private extension ProductMenuOrderViewController {
56+
57+
func configureNavigationBar() {
58+
title = NSLocalizedString("Menu Order", comment: "Product Menu Order navigation title")
59+
removeNavigationBackBarButtonText()
60+
}
61+
62+
func configureMainView() {
63+
view.backgroundColor = .listBackground
64+
}
65+
66+
func configureTableView() {
67+
tableView.register(TextFieldTableViewCell.loadNib(), forCellReuseIdentifier: TextFieldTableViewCell.reuseIdentifier)
68+
69+
tableView.dataSource = self
70+
tableView.delegate = self
71+
72+
tableView.backgroundColor = .listBackground
73+
tableView.removeLastCellSeparator()
74+
75+
tableView.sectionHeaderHeight = UITableView.automaticDimension
76+
tableView.sectionFooterHeight = UITableView.automaticDimension
77+
78+
tableView.allowsSelection = false
79+
}
80+
81+
/// Since there is only a text field in this view, the text field become the first responder immediately when the view did appear
82+
///
83+
func configureFirstTextFieldAsFirstResponder() {
84+
if let indexPath = sections.indexPathForRow(.menuOrder) {
85+
let cell = tableView.cellForRow(at: indexPath) as? TextFieldTableViewCell
86+
cell?.textField.becomeFirstResponder()
87+
}
88+
}
89+
}
90+
91+
// MARK: - UITableViewDataSource Conformance
92+
//
93+
extension ProductMenuOrderViewController: UITableViewDataSource {
94+
95+
func numberOfSections(in tableView: UITableView) -> Int {
96+
return sections.count
97+
}
98+
99+
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
100+
return sections[section].rows.count
101+
}
102+
103+
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
104+
let row = sections[indexPath.section].rows[indexPath.row]
105+
106+
let cell = tableView.dequeueReusableCell(withIdentifier: row.reuseIdentifier, for: indexPath)
107+
configure(cell, for: row, at: indexPath)
108+
109+
return cell
110+
}
111+
}
112+
113+
// MARK: - UITableViewDelegate Conformance
114+
//
115+
extension ProductMenuOrderViewController: UITableViewDelegate {
116+
117+
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
118+
return sections[section].footer
119+
}
120+
}
121+
122+
// MARK: - Support for UITableViewDataSource
123+
//
124+
private extension ProductMenuOrderViewController {
125+
/// Configure cellForRowAtIndexPath:
126+
///
127+
func configure(_ cell: UITableViewCell, for row: Row, at indexPath: IndexPath) {
128+
switch cell {
129+
case let cell as TextFieldTableViewCell:
130+
configureMenuOrder(cell: cell)
131+
default:
132+
fatalError("Unidentified product menu order row type")
133+
}
134+
}
135+
136+
func configureMenuOrder(cell: TextFieldTableViewCell) {
137+
cell.accessoryType = .none
138+
139+
let placeholder = NSLocalizedString("Menu order", comment: "Placeholder in the Product Menu Order row on Edit Product Menu Order screen.")
140+
141+
let viewModel = TextFieldTableViewCell.ViewModel(text: String(productSettings.menuOrder),
142+
placeholder: placeholder,
143+
onTextChange: { [weak self] menuOrder in
144+
if let newMenuOrder = Int(menuOrder ?? "0") {
145+
self?.productSettings.menuOrder = newMenuOrder
146+
}
147+
}, onTextDidBeginEditing: {
148+
//TODO: Add analytics track
149+
}, inputFormatter: IntegerInputFormatter(defaultValue: ""))
150+
cell.configure(viewModel: viewModel)
151+
cell.textField.applyBodyStyle()
152+
cell.textField.keyboardType = .numbersAndPunctuation
153+
}
154+
}
155+
156+
// MARK: - Constants
157+
//
158+
private extension ProductMenuOrderViewController {
159+
160+
/// Table Rows
161+
///
162+
enum Row {
163+
/// Listed in the order they appear on screen
164+
case menuOrder
165+
166+
var reuseIdentifier: String {
167+
switch self {
168+
case .menuOrder:
169+
return TextFieldTableViewCell.reuseIdentifier
170+
}
171+
}
172+
}
173+
174+
/// Table Sections
175+
///
176+
struct Section: RowIterable {
177+
let footer: String?
178+
let rows: [Row]
179+
180+
init(footer: String? = nil, rows: [Row]) {
181+
self.footer = footer
182+
self.rows = rows
183+
}
184+
}
185+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
3+
<device id="retina6_1" orientation="portrait" appearance="light"/>
4+
<dependencies>
5+
<deployment identifier="iOS"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/>
7+
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
8+
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
9+
</dependencies>
10+
<objects>
11+
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ProductMenuOrderViewController" customModule="WooCommerce" customModuleProvider="target">
12+
<connections>
13+
<outlet property="tableView" destination="JYX-ph-feD" id="lvy-s7-NCl"/>
14+
<outlet property="view" destination="1WG-jm-qSJ" id="JEY-Ml-9u5"/>
15+
</connections>
16+
</placeholder>
17+
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
18+
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="1WG-jm-qSJ">
19+
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
20+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
21+
<subviews>
22+
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="JYX-ph-feD">
23+
<rect key="frame" x="0.0" y="0.0" width="414" height="862"/>
24+
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
25+
</tableView>
26+
</subviews>
27+
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
28+
<constraints>
29+
<constraint firstItem="lyj-b5-wwJ" firstAttribute="trailing" secondItem="JYX-ph-feD" secondAttribute="trailing" id="OP9-yX-CJq"/>
30+
<constraint firstItem="JYX-ph-feD" firstAttribute="top" secondItem="1WG-jm-qSJ" secondAttribute="top" id="OqY-do-8zE"/>
31+
<constraint firstItem="lyj-b5-wwJ" firstAttribute="bottom" secondItem="JYX-ph-feD" secondAttribute="bottom" id="VIY-IS-Xpc"/>
32+
<constraint firstItem="JYX-ph-feD" firstAttribute="leading" secondItem="1WG-jm-qSJ" secondAttribute="leading" id="ttm-uH-pnW"/>
33+
</constraints>
34+
<viewLayoutGuide key="safeArea" id="lyj-b5-wwJ"/>
35+
<point key="canvasLocation" x="404" y="129"/>
36+
</view>
37+
</objects>
38+
</document>

0 commit comments

Comments
 (0)