Skip to content

Commit aa11d09

Browse files
committed
Add back InboxNoteRow and its view model.
1 parent a1d5102 commit aa11d09

File tree

5 files changed

+181
-6
lines changed

5 files changed

+181
-6
lines changed

WooCommerce/Classes/ViewRelated/Inbox/Inbox.swift

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,16 @@ struct Inbox: View {
1515
Group {
1616
switch viewModel.syncState {
1717
case .results:
18-
// TODO: 5954 - update results state
1918
InfiniteScrollList(isLoading: viewModel.shouldShowBottomActivityIndicator,
2019
loadAction: viewModel.onLoadNextPageAction) {
21-
ForEach(viewModel.notes, id: \.id) { note in
22-
TitleAndSubtitleRow(title: note.title, subtitle: note.content)
20+
ForEach(viewModel.noteRowViewModels) { rowViewModel in
21+
if #available(iOS 15.0, *) {
22+
// In order to show full-width separator, the default list separator is hidden and a `Divider` is shown inside the row.
23+
InboxNoteRow(viewModel: rowViewModel)
24+
.listRowSeparator(.hidden)
25+
} else {
26+
InboxNoteRow(viewModel: rowViewModel)
27+
}
2328
}
2429
}
2530
case .empty:
@@ -29,8 +34,14 @@ struct Inbox: View {
2934
image: .emptyProductsTabImage)
3035
.frame(maxHeight: .infinity)
3136
case .syncingFirstPage:
32-
// TODO: 5954 - update placeholder state
33-
EmptyView()
37+
List {
38+
ForEach(viewModel.placeholderRowViewModels) { rowViewModel in
39+
InboxNoteRow(viewModel: rowViewModel)
40+
.redacted(reason: .placeholder)
41+
.shimmering()
42+
}
43+
}
44+
.listStyle(PlainListStyle())
3445
}
3546
}
3647
.background(Color(.listBackground).ignoresSafeArea())
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import SwiftUI
2+
import Yosemite
3+
4+
/// Shows information about an inbox note with actions and a CTA to dismiss the note.
5+
struct InboxNoteRow: View {
6+
let viewModel: InboxNoteRowViewModel
7+
8+
var body: some View {
9+
VStack(spacing: 0) {
10+
VStack(alignment: .leading,
11+
spacing: Constants.verticalSpacing) {
12+
// HStack with type icon and relative date.
13+
// TODO: 5954 - type icon and relative date
14+
15+
// Title.
16+
Text(viewModel.title)
17+
.bodyStyle()
18+
.fixedSize(horizontal: false, vertical: true)
19+
20+
// Content.
21+
// TODO: 5954 - HTML content
22+
23+
// HStack with actions and dismiss action.
24+
HStack(spacing: Constants.spacingBetweenActions) {
25+
ForEach(viewModel.actions) { action in
26+
if let url = action.url {
27+
Button(action.title) {
28+
// TODO: 5955 - handle action
29+
print("Handling action with URL: \(url)")
30+
}
31+
.foregroundColor(Color(.accent))
32+
.font(.body)
33+
.buttonStyle(PlainButtonStyle())
34+
} else {
35+
Text(action.title)
36+
}
37+
}
38+
Button(Localization.dismiss) {
39+
// TODO: 5955 - handle dismiss action
40+
print("Handling dismiss action")
41+
}
42+
.foregroundColor(Color(.withColorStudio(.gray, shade: .shade30)))
43+
.font(.body)
44+
.buttonStyle(PlainButtonStyle())
45+
46+
Spacer()
47+
}
48+
}
49+
.padding(Constants.defaultPadding)
50+
51+
if #available(iOS 15.0, *) {
52+
// In order to show full-width separator, the default list separator is hidden and a `Divider` is shown inside the row.
53+
Divider()
54+
.frame(height: Constants.dividerHeight)
55+
}
56+
}
57+
.listRowInsets(.zero)
58+
}
59+
}
60+
61+
private extension InboxNoteRow {
62+
enum Localization {
63+
static let dismiss = NSLocalizedString("Dismiss", comment: "Dismiss button in inbox note row.")
64+
}
65+
66+
enum Constants {
67+
static let spacingBetweenActions: CGFloat = 16
68+
static let verticalSpacing: CGFloat = 14
69+
static let defaultPadding: CGFloat = 16
70+
static let dividerHeight: CGFloat = 1
71+
}
72+
}
73+
74+
struct InboxNoteRow_Previews: PreviewProvider {
75+
static var previews: some View {
76+
let note = InboxNote(siteID: 2,
77+
id: 6,
78+
name: "",
79+
type: "",
80+
status: "",
81+
actions: [.init(id: 2, name: "", label: "Let your customers know about Apple Pay", status: "", url: "https://wordpress.org"),
82+
.init(id: 6, name: "", label: "No URL", status: "", url: "")],
83+
title: "Boost sales this holiday season with Apple Pay!",
84+
content: """
85+
Increase your conversion rate by letting your customers know that you accept Apple Pay.
86+
It’s seamless to <a href=\"https://docs.woocommerce.com/document/payments/apple-pay/\">
87+
enable Apple Pay with WooCommerce Payments</a> and easy to communicate it with
88+
this <a href=\"https://developer.apple.com/apple-pay/marketing/\">marketing guide</a>.
89+
""",
90+
isRemoved: false,
91+
isRead: false,
92+
dateCreated: .init())
93+
let viewModel = InboxNoteRowViewModel(note: note)
94+
Group {
95+
InboxNoteRow(viewModel: viewModel)
96+
.preferredColorScheme(.dark)
97+
InboxNoteRow(viewModel: viewModel)
98+
.preferredColorScheme(.light)
99+
InboxNoteRow(viewModel: viewModel)
100+
.preferredColorScheme(.light)
101+
.environment(\.sizeCategory, .extraExtraExtraLarge)
102+
}
103+
}
104+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import Yosemite
2+
3+
/// View model for `InboxNoteRow`.
4+
struct InboxNoteRowViewModel: Identifiable {
5+
let id: Int64
6+
let title: String
7+
8+
let actions: [InboxNoteRowActionViewModel]
9+
10+
init(note: InboxNote) {
11+
let actions = note.actions.map { InboxNoteRowActionViewModel(action: $0) }
12+
self.init(id: note.id,
13+
title: note.title,
14+
actions: actions)
15+
}
16+
17+
init(id: Int64, title: String, actions: [InboxNoteRowActionViewModel]) {
18+
self.id = id
19+
self.title = title
20+
self.actions = actions
21+
}
22+
}
23+
24+
/// View model for an action in `InboxNoteRow`.
25+
struct InboxNoteRowActionViewModel: Identifiable {
26+
let id: Int64
27+
let title: String
28+
let url: URL?
29+
30+
init(action: InboxAction) {
31+
let url = URL(string: action.url)
32+
self.init(id: action.id, title: action.label, url: url)
33+
}
34+
35+
init(id: Int64, title: String, url: URL?) {
36+
self.id = id
37+
self.title = title
38+
self.url = url
39+
}
40+
}

WooCommerce/Classes/ViewRelated/Inbox/InboxViewModel.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,17 @@ final class InboxViewModel: ObservableObject {
88
let onLoadTrigger: PassthroughSubject<Void, Never> = PassthroughSubject()
99

1010
/// All inbox notes.
11-
@Published private(set) var notes: [InboxNote] = []
11+
@Published private var notes: [InboxNote] = []
12+
13+
/// View models for inbox note rows.
14+
@Published private(set) var noteRowViewModels: [InboxNoteRowViewModel] = []
15+
16+
/// View models for placeholder rows.
17+
let placeholderRowViewModels: [InboxNoteRowViewModel] = [Int64](0..<3).map {
18+
// The content does not matter because the text in placeholder rows is redacted.
19+
InboxNoteRowViewModel(id: $0, title: " ",
20+
actions: [.init(id: 0, title: "Placeholder", url: nil)])
21+
}
1222

1323
// MARK: Sync
1424

@@ -36,6 +46,8 @@ final class InboxViewModel: ObservableObject {
3646
self.syncState = syncState
3747
self.paginationTracker = PaginationTracker(pageSize: pageSize)
3848

49+
$notes.map { $0.map { InboxNoteRowViewModel(note: $0) } }.assign(to: &$noteRowViewModels)
50+
3951
configurePaginationTracker()
4052
configureFirstPageLoad()
4153
}

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@
211211
02645D7D27BA027B0065DC68 /* Inbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02645D7927BA027B0065DC68 /* Inbox.swift */; };
212212
02645D7E27BA027B0065DC68 /* InboxViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02645D7A27BA027B0065DC68 /* InboxViewModel.swift */; };
213213
02645D8227BA20A30065DC68 /* InboxViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02645D8127BA20A30065DC68 /* InboxViewModelTests.swift */; };
214+
02645D8527BA2DB40065DC68 /* InboxNoteRowViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02645D8327BA2DB30065DC68 /* InboxNoteRowViewModel.swift */; };
215+
02645D8627BA2DB40065DC68 /* InboxNoteRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02645D8427BA2DB40065DC68 /* InboxNoteRow.swift */; };
214216
02691780232600A6002AFC20 /* ProductsTabProductViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0269177F232600A6002AFC20 /* ProductsTabProductViewModelTests.swift */; };
215217
02691782232605B9002AFC20 /* PaginatedListViewControllerStateCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02691781232605B9002AFC20 /* PaginatedListViewControllerStateCoordinatorTests.swift */; };
216218
0269576A23726304001BA0BF /* KeyboardFrameObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0269576923726304001BA0BF /* KeyboardFrameObserver.swift */; };
@@ -1837,6 +1839,8 @@
18371839
02645D7927BA027B0065DC68 /* Inbox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Inbox.swift; sourceTree = "<group>"; };
18381840
02645D7A27BA027B0065DC68 /* InboxViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InboxViewModel.swift; sourceTree = "<group>"; };
18391841
02645D8127BA20A30065DC68 /* InboxViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InboxViewModelTests.swift; sourceTree = "<group>"; };
1842+
02645D8327BA2DB30065DC68 /* InboxNoteRowViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InboxNoteRowViewModel.swift; sourceTree = "<group>"; };
1843+
02645D8427BA2DB40065DC68 /* InboxNoteRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InboxNoteRow.swift; sourceTree = "<group>"; };
18401844
0269177F232600A6002AFC20 /* ProductsTabProductViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsTabProductViewModelTests.swift; sourceTree = "<group>"; };
18411845
02691781232605B9002AFC20 /* PaginatedListViewControllerStateCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginatedListViewControllerStateCoordinatorTests.swift; sourceTree = "<group>"; };
18421846
0269576923726304001BA0BF /* KeyboardFrameObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardFrameObserver.swift; sourceTree = "<group>"; };
@@ -3756,6 +3760,8 @@
37563760
children = (
37573761
02645D7927BA027B0065DC68 /* Inbox.swift */,
37583762
02645D7A27BA027B0065DC68 /* InboxViewModel.swift */,
3763+
02645D8427BA2DB40065DC68 /* InboxNoteRow.swift */,
3764+
02645D8327BA2DB30065DC68 /* InboxNoteRowViewModel.swift */,
37593765
);
37603766
path = Inbox;
37613767
sourceTree = "<group>";
@@ -8768,6 +8774,7 @@
87688774
934CB123224EAB150005CCB9 /* main.swift in Sources */,
87698775
456396AE25C81D81001F1A26 /* ShippingLabelFormViewModel.swift in Sources */,
87708776
02ECD1DF24FF48D000735BE5 /* PaginationTracker.swift in Sources */,
8777+
02645D8627BA2DB40065DC68 /* InboxNoteRow.swift in Sources */,
87718778
020886572499E643001D784E /* ProductExternalLinkViewController.swift in Sources */,
87728779
262A09812628A8F40033AD20 /* WooStyleModifiers.swift in Sources */,
87738780
DEC2962526C122DF005A056B /* ShippingLabelCustomsFormInputViewModel.swift in Sources */,
@@ -8914,6 +8921,7 @@
89148921
26FE09DD24D9F3F600B9BDF5 /* LoadingView.swift in Sources */,
89158922
457151AB243B6E8000EB2DFA /* ProductSlugViewController.swift in Sources */,
89168923
02F49ADA23BF356E00FA0BFA /* TitleAndTextFieldTableViewCell.ViewModel+State.swift in Sources */,
8924+
02645D8527BA2DB40065DC68 /* InboxNoteRowViewModel.swift in Sources */,
89178925
740382DB2267D94100A627F4 /* LargeImageTableViewCell.swift in Sources */,
89188926
CE22709F2293052700C0626C /* WebviewHelper.swift in Sources */,
89198927
02BA12852461674B008D8325 /* Optional+String.swift in Sources */,

0 commit comments

Comments
 (0)