Skip to content

Commit 9bd4a21

Browse files
committed
Merge branch 'trunk' into fix/15362-order-form-remaining-on-order-form-after-cancel-confirmation-with-unsaved-changes-breaks-recalculate
2 parents 3abd9a1 + cafb7b3 commit 9bd4a21

File tree

53 files changed

+1915
-427
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1915
-427
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<!--
22
Contains editorialized release notes. Raw release notes should go into `RELEASE-NOTES.txt`.
33
-->
4+
## 22.0
5+
This update adds the option to set different notification preferences for each store. In Orders and Products, your filters stay around as you move through the app, and we've fixed a crash that could happen when you choose pictures for your products. Payments is improved too, with support for Puerto Rico in WooPayments, and better accessibility.
6+
47
## 21.9
58
We're dedicated to enhancing your WooCommerce app experience! We've ironed out the issues related to discarding images during uploads in the Product Form. Plus, we've ramped up our login flow accessibility to make your journey smoother and more intuitive. Enjoy the fresh update and newly polished features!
69

Experiments/Experiments/DefaultFeatureFlagService.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,11 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
9292
case .hideSitesInStorePicker:
9393
return true
9494
case .filterHistoryOnOrderAndProductLists:
95-
return buildConfig == .localDeveloper || buildConfig == .alpha
95+
return true
9696
case .backgroundProductImageUpload:
9797
return buildConfig == .localDeveloper || buildConfig == .alpha
9898
case .notificationSettings:
99-
return buildConfig == .localDeveloper || buildConfig == .alpha
99+
return true
100100
default:
101101
return true
102102
}

Networking/Networking/Model/Copiable/Models+Copiable.generated.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,51 @@ extension Networking.NoteRange {
13111311
}
13121312
}
13131313

1314+
extension Networking.NotificationSettings {
1315+
public func copy(
1316+
blogs: CopiableProp<[NotificationSettings.Blog]> = .copy
1317+
) -> Networking.NotificationSettings {
1318+
let blogs = blogs ?? self.blogs
1319+
1320+
return Networking.NotificationSettings(
1321+
blogs: blogs
1322+
)
1323+
}
1324+
}
1325+
1326+
extension Networking.NotificationSettings.Blog {
1327+
public func copy(
1328+
blogID: CopiableProp<Int64> = .copy,
1329+
devices: CopiableProp<[NotificationSettings.Device]> = .copy
1330+
) -> Networking.NotificationSettings.Blog {
1331+
let blogID = blogID ?? self.blogID
1332+
let devices = devices ?? self.devices
1333+
1334+
return Networking.NotificationSettings.Blog(
1335+
blogID: blogID,
1336+
devices: devices
1337+
)
1338+
}
1339+
}
1340+
1341+
extension Networking.NotificationSettings.Device {
1342+
public func copy(
1343+
deviceID: CopiableProp<Int64> = .copy,
1344+
newComment: CopiableProp<Bool> = .copy,
1345+
storeOrder: CopiableProp<Bool> = .copy
1346+
) -> Networking.NotificationSettings.Device {
1347+
let deviceID = deviceID ?? self.deviceID
1348+
let newComment = newComment ?? self.newComment
1349+
let storeOrder = storeOrder ?? self.storeOrder
1350+
1351+
return Networking.NotificationSettings.Device(
1352+
deviceID: deviceID,
1353+
newComment: newComment,
1354+
storeOrder: storeOrder
1355+
)
1356+
}
1357+
}
1358+
13141359
extension Networking.Order {
13151360
public func copy(
13161361
siteID: CopiableProp<Int64> = .copy,

Networking/Networking/Model/NotificationSettings.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import Foundation
2+
import Codegen
23

34
/// Notification settings for a user
45
///
5-
public struct NotificationSettings: Equatable, Codable {
6+
public struct NotificationSettings: Equatable, Codable, GeneratedCopiable {
67

78
/// Settings for different blogs connected to the user.
89
public let blogs: [Blog]
@@ -36,7 +37,7 @@ public struct NotificationSettings: Equatable, Codable {
3637

3738
public extension NotificationSettings {
3839
/// Notification settings for a blog
39-
struct Blog: Equatable, Codable {
40+
struct Blog: Equatable, Codable, GeneratedCopiable {
4041
/// ID of the blog
4142
public let blogID: Int64
4243

@@ -75,7 +76,8 @@ public extension NotificationSettings {
7576
}
7677

7778
/// Notification settings for a device
78-
struct Device: Equatable, Codable {
79+
struct Device: Equatable, Codable, GeneratedCopiable {
80+
7981
/// Unique ID of the device
8082
public let deviceID: Int64
8183

@@ -85,6 +87,12 @@ public extension NotificationSettings {
8587
/// Whether a notification should be sent when there is a new order on the store.
8688
public let storeOrder: Bool
8789

90+
public init(deviceID: Int64, newComment: Bool, storeOrder: Bool) {
91+
self.deviceID = deviceID
92+
self.newComment = newComment
93+
self.storeOrder = storeOrder
94+
}
95+
8896
enum CodingKeys: String, CodingKey {
8997
case deviceID = "device_id"
9098
case newComment = "new_comment"

RELEASE-NOTES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
- [*] Product Form: Fix crash related to picking photos [https://github.com/woocommerce/woocommerce-ios/pull/15275]
1313
- [internal] Assign `siteID` and `productID` to image product upload statuses. [https://github.com/woocommerce/woocommerce-ios/pull/15196]
1414
- [*] Payments: Improved payment views' adaptability to larger accessibility font sizes [https://github.com/woocommerce/woocommerce-ios/pull/15328].
15+
- [*] Added an entry point to Troubleshoot Connection from the Settings screen. [https://github.com/woocommerce/woocommerce-ios/pull/15391]
16+
- [**] Filters in Orders and Products lists are now persisted. [https://github.com/woocommerce/woocommerce-ios/issues/14791]
1517
- [internal] Add site related properties to crowdsignal surveys. [https://github.com/woocommerce/woocommerce-ios/pull/15353]
18+
- [**] Notification settings are now available for configuring notifications for each store. [https://github.com/woocommerce/woocommerce-ios/pull/15386]
1619

1720
21.9
1821
-----

WooCommerce/Classes/Analytics/WooAnalyticsEvent+ConnectivityTool.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ extension WooAnalyticsEvent {
2020
.init(statName: .ordersListTopBannerTroubleshootTapped, properties: [:])
2121
}
2222

23+
static func settingsTroubleshootTapped() -> WooAnalyticsEvent {
24+
.init(statName: .settingsTroubleshootConnectionTapped, properties: [:])
25+
}
26+
2327
static func requestResponse(test: Test, success: Bool, timeTaken: Double) -> WooAnalyticsEvent {
2428
.init(statName: .connectivityToolRequestResponse,
2529
properties: [

WooCommerce/Classes/Analytics/WooAnalyticsStat.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ enum WooAnalyticsStat: String {
321321
case settingsSelectedStoreTapped = "settings_selected_site_tapped"
322322
case settingsContactSupportTapped = "main_menu_contact_support_tapped"
323323
case settingsDomainsTapped = "settings_domains_tapped"
324+
case settingsTroubleshootConnectionTapped = "settings_troubleshoot_connection_tapped"
324325

325326
case settingsBetaFeaturesButtonTapped = "settings_beta_features_button_tapped"
326327
case settingsBetaFeaturesProductsToggled = "settings_beta_features_products_toggled"
@@ -335,11 +336,19 @@ enum WooAnalyticsStat: String {
335336
case settingsThirdPartyLearnMoreTapped = "privacy_settings_third_party_tracking_info_link_tapped"
336337
case settingsLicensesLinkTapped = "settings_about_open_source_licenses_link_tapped"
337338
case settingsAboutLinkTapped = "settings_about_woocommerce_link_tapped"
339+
case settingsNotificationSettingsTapped = "settings_notification_settings_tapped"
338340

339341
case settingsLogoutTapped = "settings_logout_button_tapped"
340342
case settingsLogoutConfirmation = "settings_logout_confirmation_dialog_result"
341343
case settingsWereHiringTapped = "settings_we_are_hiring_button_tapped"
342344

345+
// MARK: Notification Settings
346+
//
347+
case notificationSettingsUpdateButtonTapped = "notification_settings_update_button_tapped"
348+
case notificationSettingsSaveButtonTapped = "notification_settings_save_button_tapped"
349+
case notificationSettingsSavingSuccess = "notification_settings_saving_success"
350+
case notificationSettingsSavingFailed = "notification_settings_saving_failed"
351+
343352
// MARK: Domain Settings
344353
//
345354
case domainSettingsStep = "custom_domains_step"

WooCommerce/Classes/ViewRelated/Connectivity Tool/ConnectivityTool.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ struct ConnectivityTool: View {
103103
.padding()
104104
}
105105
.background(Color(uiColor: .listBackground))
106+
.navigationTitle(Localization.title)
107+
.navigationBarTitleDisplayMode(.inline)
106108
}
107109
}
108110

@@ -112,6 +114,11 @@ private extension ConnectivityTool {
112114
comment: "Subtitle on the connectivity tool screen")
113115
static let contactSupport = NSLocalizedString("Contact Support",
114116
comment: "Contact support button in the connectivity tool screen")
117+
static let title = NSLocalizedString(
118+
"connectivityTool.title",
119+
value: "Troubleshoot Connection",
120+
comment: "Screen title for the connectivity tool"
121+
)
115122
}
116123
}
117124

WooCommerce/Classes/ViewRelated/Dashboard/Settings/NotificationSettings/NotificationSettingsView.swift

Lines changed: 116 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import SwiftUI
22
import struct Yosemite.Site
3+
import struct Yosemite.NotificationSettings
34

45
final class NotificationSettingsHostingController: UIHostingController<NotificationSettingsView> {
56
init() {
@@ -36,13 +37,54 @@ struct NotificationSettingsView: View {
3637
}
3738
}
3839
.navigationTitle(Localization.title)
40+
.toolbar {
41+
ToolbarItem(placement: .confirmationAction) {
42+
Button {
43+
saveSettings()
44+
} label: {
45+
if viewModel.isSavingSettings {
46+
ProgressView().progressViewStyle(.circular)
47+
} else {
48+
Text(Localization.save)
49+
}
50+
}
51+
.disabled(viewModel.hasSiteSettingsChanges == false)
52+
}
53+
}
3954
.task {
4055
await viewModel.checkNotificationPermission()
41-
await viewModel.synchronizeSites()
56+
if viewModel.currentDeviceID != nil {
57+
await viewModel.retrieveNotificationSettings()
58+
await viewModel.synchronizeSites()
59+
}
4260
}
4361
.sheet(item: $selectedSite) { site in
44-
SiteNotificationSettingsView(siteTitle: site.name)
62+
if let settings = viewModel.loadSettings(for: site) {
63+
SiteNotificationSettingsView(siteTitle: site.name,
64+
ordersNotificationsEnabled: settings.storeOrder,
65+
productReviewsNotificationsEnabled: settings.newComment,
66+
completionHandler: { newOrder, productReviews in
67+
viewModel.updateSettings(siteID: site.siteID,
68+
ordersNotificationsEnabled: newOrder,
69+
productReviewsNotificationsEnabled: productReviews)
70+
})
71+
}
72+
}
73+
.alert(Localization.errorSavingSiteSettings, isPresented: $viewModel.savingSiteSettingsFailed) {
74+
Button(Localization.cancel, role: .cancel) {}
75+
Button(Localization.retry) {
76+
saveSettings()
77+
}
4578
}
79+
.alert(Localization.errorLoadingSiteSettings, isPresented: $viewModel.loadingSiteSettingsFailed) {
80+
Button(Localization.cancel, role: .cancel) {}
81+
Button(Localization.retry) {
82+
Task {
83+
await viewModel.retrieveNotificationSettings()
84+
}
85+
}
86+
}
87+
.notice($viewModel.notice)
4688
}
4789
}
4890

@@ -88,14 +130,20 @@ private extension NotificationSettingsView {
88130
}
89131

90132
Section {
91-
ForEach(viewModel.sites) { site in
92-
detailRow(for: site)
133+
if viewModel.isLoadingSiteSettings {
134+
ActivityIndicator(isAnimating: .constant(true), style: .medium)
135+
.frame(maxWidth: .infinity)
136+
} else if viewModel.siteSettings != nil {
137+
ForEach(viewModel.sites) { site in
138+
detailRow(for: site)
139+
}
93140
}
94141
} header: {
95142
Text(Localization.storeListSectionHeader)
96143
} footer: {
97144
Text(Localization.storeListSectionFooter)
98145
}
146+
.renderedIf(viewModel.shouldShowSiteList)
99147
}
100148
}
101149

@@ -107,9 +155,11 @@ private extension NotificationSettingsView {
107155
VStack(alignment: .leading) {
108156
Text(site.name)
109157
.bodyStyle()
110-
Text(site.url)
111-
.foregroundStyle(Color.secondary)
112-
.captionStyle()
158+
if let settings = viewModel.loadSettings(for: site) {
159+
Text(description(for: settings))
160+
.foregroundStyle(Color.secondary)
161+
.captionStyle()
162+
}
113163
}
114164
.multilineTextAlignment(.leading)
115165

@@ -130,6 +180,25 @@ private extension NotificationSettingsView {
130180
// Ask the system to open that URL.
131181
await UIApplication.shared.open(url)
132182
}
183+
184+
func saveSettings() {
185+
Task {
186+
await viewModel.saveSettings()
187+
}
188+
}
189+
190+
func description(for settings: NotificationSettings.Device) -> String {
191+
switch (settings.storeOrder, settings.newComment) {
192+
case (true, true):
193+
[Localization.newOrders, Localization.productReviews].joined(separator: ", ")
194+
case (true, false):
195+
Localization.newOrders
196+
case (false, true):
197+
Localization.productReviews
198+
case (false, false):
199+
Localization.off
200+
}
201+
}
133202
}
134203

135204
extension NotificationSettingsView {
@@ -179,6 +248,46 @@ extension NotificationSettingsView {
179248
value: "Customize your notification preferences for each store.",
180249
comment: "Footer of the store list section on the notification settings view"
181250
)
251+
static let errorLoadingSiteSettings = NSLocalizedString(
252+
"notificationSettingsView.errorLoadingSiteSettings",
253+
value: "Unable to load notification settings for your stores.",
254+
comment: "Error message when loading site settings fails on the notification settings view"
255+
)
256+
static let retry = NSLocalizedString(
257+
"notificationSettingsView.retry",
258+
value: "Try again",
259+
comment: "Button to reload site settings on the notification settings view"
260+
)
261+
static let save = NSLocalizedString(
262+
"notificationSettingsView.save",
263+
value: "Save",
264+
comment: "Button to save site settings on the notification settings view"
265+
)
266+
static let cancel = NSLocalizedString(
267+
"notificationSettingsView.cancel",
268+
value: "Cancel",
269+
comment: "Button to cancel saving site settings on the notification settings view"
270+
)
271+
static let errorSavingSiteSettings = NSLocalizedString(
272+
"notificationSettingsView.errorSavingSiteSettings",
273+
value: "Unable to save notification settings",
274+
comment: "Error message when saving site settings fails on the notification settings view"
275+
)
276+
static let newOrders = NSLocalizedString(
277+
"notificationSettingsView.newOrders",
278+
value: "New orders",
279+
comment: "Label indicating that new orders notifications are enabled for a site on the notification settings view"
280+
)
281+
static let productReviews = NSLocalizedString(
282+
"notificationSettingsView.productReviews",
283+
value: "Product reviews",
284+
comment: "Label indicating that product reviews notifications are enabled for a site on the notification settings view"
285+
)
286+
static let off = NSLocalizedString(
287+
"notificationSettingsView.off",
288+
value: "Off",
289+
comment: "Label indicating that notifications are disabled for a site on the notification settings view"
290+
)
182291
}
183292
}
184293

0 commit comments

Comments
 (0)