Skip to content

Commit 7773dd1

Browse files
Merge pull request #431 from woocommerce/issue/19-wiring-order-details
Notifications: New Order Details
2 parents f9b871d + 9ba12a0 commit 7773dd1

File tree

7 files changed

+296
-4
lines changed

7 files changed

+296
-4
lines changed

Networking/Networking/Model/MetaContainer.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ extension MetaContainer {
4949
public enum Keys: String {
5050
case comment
5151
case home
52+
case order
5253
case post
5354
case reply = "reply_comment"
5455
case site
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Foundation
2+
import UIKit
3+
4+
5+
// MARK: - UIStoryboard Woo Methods
6+
//
7+
extension UIStoryboard {
8+
9+
/// Returns a (new) instance of the Orders Storyboard.
10+
///
11+
static var orders: UIStoryboard {
12+
return UIStoryboard(name: "Orders", bundle: .main)
13+
}
14+
}

WooCommerce/Classes/ViewRelated/Notifications/NotificationsViewController.swift

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,37 @@ extension NotificationsViewController: UITableViewDelegate {
262262
tableView.deselectRow(at: indexPath, animated: true)
263263

264264
let note = resultsController.object(at: indexPath)
265-
let detailsViewController = NotificationDetailsViewController(note: note)
266265

266+
switch note.kind {
267+
case .storeOrder:
268+
presentOrderDetails(for: note)
269+
default:
270+
presentNotificationDetails(for: note)
271+
}
272+
}
273+
}
274+
275+
276+
// MARK: - Details Rendering
277+
//
278+
private extension NotificationsViewController {
279+
280+
/// Pushes the Order Details associated to a given Note (if possible).
281+
///
282+
func presentOrderDetails(for note: Note) {
283+
guard let orderID = note.meta.identifier(forKey: .order), let siteID = note.meta.identifier(forKey: .site) else {
284+
DDLogError("## Notification with [\(note.noteId)] lacks its OrderID!")
285+
return
286+
}
287+
288+
let loaderViewController = OrderLoaderViewController(orderID: orderID, siteID: siteID)
289+
navigationController?.pushViewController(loaderViewController, animated: true)
290+
}
291+
292+
/// Pushes the Notification Details associated to a given Note.
293+
///
294+
func presentNotificationDetails(for note: Note) {
295+
let detailsViewController = NotificationDetailsViewController(note: note)
267296
navigationController?.pushViewController(detailsViewController, animated: true)
268297
}
269298
}

WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderDetailsViewController.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import Yosemite
66
import CocoaLumberjack
77

88

9+
// MARK: - OrderDetailsViewController: Displays the details for a given Order.
10+
//
911
class OrderDetailsViewController: UIViewController {
1012

1113
/// Main TableView.
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
import Foundation
2+
import UIKit
3+
import Yosemite
4+
5+
6+
// MARK: - OrderLoaderViewController: Loads asynchronously an Order (given it's OrderID + SiteID).
7+
// On Success the OrderDetailsViewController will be rendered "in place".
8+
//
9+
class OrderLoaderViewController: UIViewController {
10+
11+
/// UI Spinner
12+
///
13+
private let activityIndicator = UIActivityIndicatorView(style: .gray)
14+
15+
/// Target OrderID
16+
///
17+
private let orderID: Int
18+
19+
/// Target Order's SiteID
20+
///
21+
private let siteID: Int
22+
23+
/// UI Active State
24+
///
25+
private var state: State = .loading {
26+
didSet {
27+
didLeave(state: oldValue)
28+
didEnter(state: state)
29+
}
30+
}
31+
32+
33+
// MARK: - Initializers
34+
35+
init(orderID: Int, siteID: Int) {
36+
self.orderID = orderID
37+
self.siteID = siteID
38+
39+
super.init(nibName: nil, bundle: nil)
40+
}
41+
42+
required init?(coder aDecoder: NSCoder) {
43+
fatalError("Please specify the OrderID and SiteID!")
44+
}
45+
46+
47+
// MARK: - Overridden Methods
48+
49+
override func viewDidLoad() {
50+
super.viewDidLoad()
51+
52+
configureNavigationItem()
53+
configureSpinner()
54+
configureMainView()
55+
56+
reloadOrder()
57+
}
58+
}
59+
60+
61+
// MARK: - Actions
62+
//
63+
private extension OrderLoaderViewController {
64+
65+
/// Loads (and displays) the specified Order.
66+
///
67+
func reloadOrder() {
68+
let action = OrderAction.retrieveOrder(siteID: siteID, orderID: orderID) { [weak self] (order, error) in
69+
guard let `self` = self else {
70+
return
71+
}
72+
73+
guard let order = order else {
74+
DDLogError("## Error loading Order \(self.siteID).\(self.orderID): \(error.debugDescription)")
75+
self.state = .failure
76+
return
77+
}
78+
79+
self.state = .success(order: order)
80+
}
81+
82+
state = .loading
83+
StoresManager.shared.dispatch(action)
84+
}
85+
}
86+
87+
88+
// MARK: - Configuration
89+
//
90+
private extension OrderLoaderViewController {
91+
92+
/// Setup: Navigation
93+
///
94+
func configureNavigationItem() {
95+
title = NSLocalizedString("Loading Order", comment: "Displayed when an Order is being retrieved")
96+
navigationItem.backBarButtonItem = UIBarButtonItem(title: String(), style: .plain, target: nil, action: nil)
97+
}
98+
99+
/// Setup: Main View
100+
///
101+
func configureMainView() {
102+
view.backgroundColor = StyleManager.tableViewBackgroundColor
103+
view.addSubview(activityIndicator)
104+
view.pinSubviewAtCenter(activityIndicator)
105+
}
106+
107+
/// Setup: Spinner
108+
///
109+
func configureSpinner() {
110+
activityIndicator.hidesWhenStopped = true
111+
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
112+
}
113+
}
114+
115+
116+
// MARK: - Overlays
117+
//
118+
private extension OrderLoaderViewController {
119+
120+
/// Starts the Spinner
121+
///
122+
func startSpinner() {
123+
activityIndicator.startAnimating()
124+
}
125+
126+
/// Stops the Spinner
127+
///
128+
func stopSpinner() {
129+
activityIndicator.stopAnimating()
130+
}
131+
132+
/// Displays the Loading Overlay.
133+
///
134+
func displayFailureOverlay() {
135+
let overlayView: OverlayMessageView = OverlayMessageView.instantiateFromNib()
136+
overlayView.messageImage = .waitingForCustomersImage
137+
overlayView.messageText = NSLocalizedString("The Order couldn't be loaded!", comment: "Fetching an Order Failed")
138+
overlayView.actionText = NSLocalizedString("Retry", comment: "Retry the last action")
139+
overlayView.onAction = { [weak self] in
140+
self?.reloadOrder()
141+
}
142+
143+
overlayView.attach(to: view)
144+
}
145+
146+
/// Removes all of the the OverlayMessageView instances in the view hierarchy.
147+
///
148+
func removeAllOverlays() {
149+
for subview in view.subviews where subview is OverlayMessageView {
150+
subview.removeFromSuperview()
151+
}
152+
}
153+
154+
/// Presents the OrderDetailsViewController, as a childViewController, for a given Order.
155+
///
156+
func presentOrderDetails(for order: Order) {
157+
let identifier = OrderDetailsViewController.classNameWithoutNamespaces
158+
guard let detailsViewController = UIStoryboard.orders.instantiateViewController(withIdentifier: identifier) as? OrderDetailsViewController else {
159+
fatalError()
160+
}
161+
162+
// Setup the DetailsViewController
163+
detailsViewController.viewModel = OrderDetailsViewModel(order: order)
164+
165+
// Attach
166+
addChild(detailsViewController)
167+
attachSubview(detailsViewController.view)
168+
detailsViewController.didMove(toParent: self)
169+
170+
// And, of course, borrow the Child's Title
171+
title = detailsViewController.title
172+
}
173+
174+
/// Removes all of the children UIViewControllers
175+
///
176+
func detachChildrenViewControllers() {
177+
for child in children {
178+
child.view.removeFromSuperview()
179+
child.removeFromParent()
180+
child.didMove(toParent: nil)
181+
}
182+
}
183+
}
184+
185+
186+
// MARK: - UI Methods
187+
//
188+
private extension OrderLoaderViewController {
189+
190+
/// Attaches a given Subview, and ensures it's pinned to all the edges
191+
///
192+
func attachSubview(_ subview: UIView) {
193+
subview.translatesAutoresizingMaskIntoConstraints = false
194+
view.addSubview(subview)
195+
view.pinSubviewToAllEdges(subview)
196+
}
197+
}
198+
199+
200+
// MARK: - Finite State Machine Management
201+
//
202+
private extension OrderLoaderViewController {
203+
204+
/// Runs whenever the FSM enters a State.
205+
///
206+
func didEnter(state: State) {
207+
switch state {
208+
case .loading:
209+
startSpinner()
210+
case .success(let order):
211+
presentOrderDetails(for: order)
212+
case .failure:
213+
displayFailureOverlay()
214+
}
215+
}
216+
217+
/// Runs whenever the FSM leaves a State.
218+
///
219+
func didLeave(state: State) {
220+
switch state {
221+
case .loading:
222+
stopSpinner()
223+
case .success(_):
224+
detachChildrenViewControllers()
225+
case .failure:
226+
removeAllOverlays()
227+
}
228+
}
229+
}
230+
231+
232+
// MARK: - OrderLoader Possible Status(es)
233+
//
234+
private enum State {
235+
case loading
236+
case success(order: Order)
237+
case failure
238+
}

WooCommerce/Classes/ViewRelated/Orders/Orders.storyboard

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="QmM-AQ-tMl">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="QmM-AQ-tMl">
33
<device id="retina4_7" orientation="portrait">
44
<adaptation id="fullscreen"/>
55
</device>
66
<dependencies>
77
<deployment identifier="iOS"/>
8-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
8+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
99
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
1010
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
1111
</dependencies>
@@ -98,7 +98,7 @@
9898
<!--Single Order-->
9999
<scene sceneID="p5z-EU-84P">
100100
<objects>
101-
<viewController storyboardIdentifier="SingleOrderViewController" id="IgL-pI-DQK" userLabel="Single Order" customClass="OrderDetailsViewController" customModule="WooCommerce" customModuleProvider="target" sceneMemberID="viewController">
101+
<viewController storyboardIdentifier="OrderDetailsViewController" id="IgL-pI-DQK" userLabel="Single Order" customClass="OrderDetailsViewController" customModule="WooCommerce" customModuleProvider="target" sceneMemberID="viewController">
102102
<view key="view" contentMode="scaleToFill" id="BSM-Pn-wcY">
103103
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
104104
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262
B53A569D21123EEB000776C9 /* MockupStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53A569C21123EEB000776C9 /* MockupStorage.swift */; };
6363
B53A56A22112470C000776C9 /* MockupStorage+Sample.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53A56A12112470C000776C9 /* MockupStorage+Sample.swift */; };
6464
B53A56A42112483E000776C9 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53A56A32112483D000776C9 /* Constants.swift */; };
65+
B53B3F37219C75AC00DF1EB6 /* OrderLoaderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B3F36219C75AC00DF1EB6 /* OrderLoaderViewController.swift */; };
66+
B53B3F39219C817800DF1EB6 /* UIStoryboard+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B3F38219C817800DF1EB6 /* UIStoryboard+Woo.swift */; };
6567
B53B898920D450AF00EDB467 /* SessionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B898820D450AF00EDB467 /* SessionManagerTests.swift */; };
6668
B53B898D20D462A000EDB467 /* StoresManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B898C20D462A000EDB467 /* StoresManager.swift */; };
6769
B541B2132189E7FD008FE7C1 /* ScannerWooTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B541B2122189E7FD008FE7C1 /* ScannerWooTests.swift */; };
@@ -300,6 +302,8 @@
300302
B53A569C21123EEB000776C9 /* MockupStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MockupStorage.swift; path = ../../../Yosemite/YosemiteTests/Mockups/MockupStorage.swift; sourceTree = "<group>"; };
301303
B53A56A12112470C000776C9 /* MockupStorage+Sample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "MockupStorage+Sample.swift"; path = "../../../Yosemite/YosemiteTests/Mockups/MockupStorage+Sample.swift"; sourceTree = "<group>"; };
302304
B53A56A32112483D000776C9 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = ../../Yosemite/YosemiteTests/Settings/Constants.swift; sourceTree = "<group>"; };
305+
B53B3F36219C75AC00DF1EB6 /* OrderLoaderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderLoaderViewController.swift; sourceTree = "<group>"; };
306+
B53B3F38219C817800DF1EB6 /* UIStoryboard+Woo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStoryboard+Woo.swift"; sourceTree = "<group>"; };
303307
B53B898820D450AF00EDB467 /* SessionManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManagerTests.swift; sourceTree = "<group>"; };
304308
B53B898C20D462A000EDB467 /* StoresManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoresManager.swift; sourceTree = "<group>"; };
305309
B541B2122189E7FD008FE7C1 /* ScannerWooTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScannerWooTests.swift; sourceTree = "<group>"; };
@@ -927,6 +931,7 @@
927931
B5AA7B3E20ED81C2004DA14F /* UserDefaults+Woo.swift */,
928932
CE021534215BE3AB00C19555 /* LoginNavigationController+Woo.swift */,
929933
B59D49CC219B587E006BF0AD /* UILabel+OrderStatus.swift */,
934+
B53B3F38219C817800DF1EB6 /* UIStoryboard+Woo.swift */,
930935
);
931936
path = Extensions;
932937
sourceTree = "<group>";
@@ -1028,6 +1033,7 @@
10281033
B557DA1320979904005962F4 /* CustomerNoteTableViewCell.swift */,
10291034
B557DA1420979904005962F4 /* CustomerNoteTableViewCell.xib */,
10301035
CEE006072077D14C0079161F /* OrderDetailsViewController.swift */,
1036+
B53B3F36219C75AC00DF1EB6 /* OrderLoaderViewController.swift */,
10311037
CE1EC8EF20B8A408009762BF /* OrderNoteTableViewCell.swift */,
10321038
CE1EC8EE20B8A408009762BF /* OrderNoteTableViewCell.xib */,
10331039
CE1EC8C320B46819009762BF /* PaymentTableViewCell.swift */,
@@ -1434,6 +1440,7 @@
14341440
B57B678A2107546E00AF8905 /* Address+Woo.swift in Sources */,
14351441
747AA0892107CEC60047A89B /* AnalyticsProvider.swift in Sources */,
14361442
B5DBF3CB20E149CC00B53AED /* AuthenticatedState.swift in Sources */,
1443+
B53B3F39219C817800DF1EB6 /* UIStoryboard+Woo.swift in Sources */,
14371444
CE27257F21925AE8002B22EB /* ValueOneTableViewCell.swift in Sources */,
14381445
747AA08B2107CF8D0047A89B /* TracksProvider.swift in Sources */,
14391446
CE1EC8F120B8A408009762BF /* OrderNoteTableViewCell.swift in Sources */,
@@ -1521,6 +1528,7 @@
15211528
CE1CCB4B20570B1F000EE3AC /* OrderListCell.swift in Sources */,
15221529
748D34DF214828DD00E21A2F /* TopPerformersViewController.swift in Sources */,
15231530
B5AA7B3D20ED5D15004DA14F /* SessionManager.swift in Sources */,
1531+
B53B3F37219C75AC00DF1EB6 /* OrderLoaderViewController.swift in Sources */,
15241532
CE24BCCF212DE8A6001CD12E /* HeadlineLabelTableViewCell.swift in Sources */,
15251533
B53B898D20D462A000EDB467 /* StoresManager.swift in Sources */,
15261534
B5D1AFC020BC67C200DB0E8C /* WooConstants.swift in Sources */,

0 commit comments

Comments
 (0)