Skip to content

Commit 367b5ab

Browse files
authored
HACK Week: Load and update site settings (#15386)
2 parents 4ce27da + 319ea15 commit 367b5ab

File tree

10 files changed

+558
-23
lines changed

10 files changed

+558
-23
lines changed

Experiments/Experiments/DefaultFeatureFlagService.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- [*] Added an entry point to Troubleshoot Connection from the Settings screen. [https://github.com/woocommerce/woocommerce-ios/pull/15391]
1111
- [**] Filters in Orders and Products lists are now persisted. [https://github.com/woocommerce/woocommerce-ios/issues/14791]
1212
- [internal] Add site related properties to crowdsignal surveys. [https://github.com/woocommerce/woocommerce-ios/pull/15353]
13+
- [**] Notification settings are now available for configuring notifications for each store. [https://github.com/woocommerce/woocommerce-ios/pull/15386]
1314

1415
21.9
1516
-----

WooCommerce/Classes/Analytics/WooAnalyticsStat.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,11 +336,19 @@ enum WooAnalyticsStat: String {
336336
case settingsThirdPartyLearnMoreTapped = "privacy_settings_third_party_tracking_info_link_tapped"
337337
case settingsLicensesLinkTapped = "settings_about_open_source_licenses_link_tapped"
338338
case settingsAboutLinkTapped = "settings_about_woocommerce_link_tapped"
339+
case settingsNotificationSettingsTapped = "settings_notification_settings_tapped"
339340

340341
case settingsLogoutTapped = "settings_logout_button_tapped"
341342
case settingsLogoutConfirmation = "settings_logout_confirmation_dialog_result"
342343
case settingsWereHiringTapped = "settings_we_are_hiring_button_tapped"
343344

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+
344352
// MARK: Domain Settings
345353
//
346354
case domainSettingsStep = "custom_domains_step"

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)